Cómo devolver todo de una columna, algo de otra en sql

Tengo las siguientes tablas:

geometría

| ID | GEOID | YEAR | VALUE |
|  1 |   100 | 2010 |    76 |
|  2 |   100 | 2020 |     1 |
|  3 |   101 | 2010 |    73 |
|  4 |   101 | 2020 |   123 |
|  5 |   102 | 2010 |     4 |
|  6 |   102 | 2020 |    20 |

años

| YEAR |
| 2010 |
| 2020 |

regiones

| ID | GEOID | REGION |
|  1 |   100 |      1 |
|  2 |   101 |      1 |
|  3 |   102 |      1 |
|  4 |   100 |      2 |
|  5 |   101 |      2 |
|  6 |   102 |      3 |

I want to get geoid, year, and value where region = 2. Here's my SQL:

SELECT DISTINCT reg.geoid, years.year, j.value
FROM regions AS reg, years
LEFT JOIN (
  SELECT geometry.geoid, geometry.year, geometry.value
  FROM geometry
  JOIN regions AS reg
  ON reg.geoid = geometry.geoid
  AND reg.region = 2
  ) AS j
ON j.year = years.year
WHERE reg.region = 1
AND reg.geoid = j.geoid

And that gives me this:

| GEOID | YEAR | VALUE |
|   100 | 2010 |    76 |
|   100 | 2020 |     1 |
|   101 | 2010 |    73 |
|   101 | 2020 |   123 |

But I want to get all geoids and all years. That's why I did the LEFT JOIN, so that it returns all of the rows of the LEFT table. I expected that it would return this:

| GEOID | YEAR | VALUE |
|   100 | 2010 |    76 |
|   100 | 2020 |     1 |
|   101 | 2010 |    73 |
|   101 | 2020 |   123 |
|   102 | 2010 |  null |
|   102 | 2020 |  null |

¿Cómo puedo conseguir esto?

Noticias

Here's a SQLFiddle of the above data: http://sqlfiddle.com/#!2/963c7/6

preguntado el 04 de septiembre de 13 a las 02:09

what is the purpose of table year? -

Seemed like the quickest way to get all the years, compared to doing a SELECT DISTINCT year FROM geometry (which is has hundreds of thousands of records) -

Do I have to do a CASE WHEN on j.value? Why doesn't it keep all of the rows from regions and years, which the subquery is left joined to? @491243 -

remove WHERE y prueba esto ON j.year = years.year AND reg.region = 1 AND reg.geoid = j.geoid -

2 Respuestas

I think you have overcomplicated your query. The following just does a left outer join de geometry a regions. When a value does not match in regions, después haz un case statement is used to return a NULL valor:

SELECT g.geoid, g.year,
       (case when r.geoid is not null then g.value end) as value
FROM geometry g left outer join
     regions r
     ON r.geoid = g.geoid AND
        r.region = 2
order by geoid, year;

Respondido el 04 de Septiembre de 13 a las 03:09

Can I do multiple LEFT OUTER JOINs? It's complicated because I need to perform aggregate functions on values from multiple regions; e.g. the subquery will have UNIONs to get values from region 1, 2, 3... etc. - bozdoz

@bozdoz . . . When I run the above query, I get exactly the results in the question. I just added an order by to the query, but that only guarantees the ordering of the results. - gordon linoff

Gotcha. Yeah, I got it to work. But I'm nervous of this answer, because I've had troubles with multiple joins. - bozdoz

For example, once I add another column, this method doesn't work. It's a great answer for the sample problem, but I need a flexible statement to work with. Check this out: sqlfiddle.com/#!2/5042d/2 note that the second row should be value = 10, but it's 0. - bozdoz

@bozdoz . . . If you have a different question, you should ask another question, with the appropriate data and requirements for it. - gordon linoff

The issue is that you've use a WHERE clause that includes the table you are trying to LEFT JOIN to. This makes it work like an INNER JOIN statement instead.

Específicamente AND reg.geoid = j.geoid.

Move that to the JOIN statement instead and you might have better luck. Of course your cross join isn't very explicit and causes errors in the SQL you gave, the following will work though:

SELECT DISTINCT regyears.geoid, regyears.year, j.value
FROM (
SELECT reg.geoid, reg.region, years.year
FROM @regions AS reg, @years AS years
) regyears
LEFT JOIN (
  SELECT geometry.geoid, geometry.year, geometry.value
  FROM @geometry AS geometry
  JOIN @regions AS reg
  ON reg.geoid = geometry.geoid
  AND reg.region = 2
  ) AS j
ON j.year = regyears.year
AND j.geoid = regyears.geoid
WHERE regyears.region = 1

I've used temporary objects to test this, thus the @regions, @years, @geometry. Just replace those with your table names.

Respondido el 04 de Septiembre de 13 a las 02:09

This needs a bit of tweaking, but I like the idea, and it seems to work: sqlfiddle.com/#!2/963c7/14 - bozdoz

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.