问题
I have a table which looks roughly like this:
+---------------------+----------+
| date | item |
+---------------------+----------+
| 2008-11-30 11:15:59 | Plums |
| 2012-11-08 19:42:37 | Lemons |
| 2013-01-30 18:58:07 | Apples |
| 2013-02-12 13:44:45 | Pears |
| 2014-06-08 11:46:48 | Apples |
| 2014-09-01 20:28:03 | Oranges |
+---------------------+----------+
I would now like to select items for two cases:
1) I'd like to select all distinct items for this year (or a different specified year) which are NEW this year, meaning that they have not appeared in previous years. This query should therefore give me "Oranges" as a result for 2014.
2) I'd also like to select all distinct items for this (or another specified) year which are RETURNING, which obviously means that they have appeared in previous years. This query should give me "Apples" as a result for 2014.
To clarify, I would like to use both queries only for comparison to previous years. So if I were to specify 2013 as the year for which this query is run, "Apples" should appear as new, not returning.
I don't necessarily need both things in one query (I doubt it's possible), so two different queries, one for each case, are perfectly fine.
回答1:
How about (SQL Fiddle) for NEW items:
SELECT DISTINCT m1.item
FROM MyTable m1
LEFT JOIN MyTable m2 ON m1.item = m2.item AND YEAR(m2.date) < 2014
WHERE YEAR(m1.date) = 2014
AND m2.date IS NULL
And (SQL Fiddle) for RETURNING items:
SELECT DISTINCT m1.item
FROM MyTable m1
INNER JOIN MyTable m2 ON m1.item = m2.item
WHERE YEAR(m1.date) = 2014
AND YEAR(m2.date) < 2014
If you want to combine the two queries together (SQL Fiddle or SQL Fiddle):
SELECT DISTINCT m1.item, 'New' AS Status
FROM MyTable m1
LEFT JOIN MyTable m2 ON m1.item = m2.item AND YEAR(m2.date) < 2014
WHERE YEAR(m1.date) = 2014
AND m2.date IS NULL
UNION ALL
SELECT DISTINCT m1.item, 'Returning' AS Status
FROM MyTable m1
INNER JOIN MyTable m2 ON m1.item = m2.item
WHERE YEAR(m1.date) = 2014
AND YEAR(m2.date) < 2014
ORDER BY Status;
回答2:
1 - This solution can use an index
SELECT DISTINCT x.item
FROM my_table x
LEFT
JOIN my_table y
ON y.item = x.item
AND y.date < x.date
WHERE x.date BETWEEN '2014-01-01' AND '2014-12-31' AND y.date IS NULL;
2 - And so can this...
SELECT DISTINCT x.item
FROM my_table x
JOIN my_table y
ON y.item = x.item
AND y.date < x.date
WHERE x.date BETWEEN '2014-01-01' AND '2014-12-31';
2 is obviously a simpler problem than 1, so I'm a little surprised you've asked for them in this order !?!?
If you remove the WHERE ... IS NULL from the first query then you in effect get the answer which you 'doubt is possible', but a UNION of these two queries will achieve the same effect.
回答3:
this will give you fruits that are in a specific year but not in other years
SELECT t.item
FROM table t
WHERE YEAR(t.date) = 2014
AND NOT EXISTS (SELECT 1 FROM table f WHERE YEAR(f.date) <> 2014 AND f.item = t.item)
this will give you ones that are in more than one year
SELECT t.item
FROM table t
WHERE YEAR(t.date) = 2014
AND EXISTS (SELECT 1 FROM table f WHERE YEAR(f.date) <> 2014 AND f.item = t.item)
you can put them all into one like this.
SELECT t.item as "NEW", f.item as 'RETURNING'
FROM
( SELECT t.item, @a := (COALESCE(@a, 0) + 1) as join_col
FROM test t
WHERE YEAR(t.date) = 2014
AND NOT EXISTS (SELECT 1 FROM test f WHERE YEAR(f.date) <> 2014 AND f.item = t.item)
) t
LEFT JOIN
( SELECT t.item , @b := (COALESCE(@b, 0) + 1) as join_col
FROM test t
WHERE YEAR(t.date) = 2014
AND EXISTS (SELECT 1 FROM test f WHERE YEAR(f.date) <> 2014 AND f.item = t.item)
) f ON f.join_col = t.join_col;
DEMO
来源:https://stackoverflow.com/questions/25853891/select-new-or-returning-items-for-a-specified-year