英文:
Having clause with aggregate and NOT IN doesn't apply both as expected
问题
我正在尝试从以下示例中选择最近三年内Vigor值为6、8或9的树木。因此,在下面的网格中,我想要所有TreeID为2和3的行,但不包括任何TreeID为1的行。
SELECT PlotID, TreeID, Vigor, count(*) c
FROM tblTreeInfo
GROUP BY PlotID, TreeID, Vigor
HAVING (c < 3 AND Vigor NOT IN (6, 8, 9))
所以,我尝试了这个,但这会排除TreeID为2,因为它的Vigor值为8,但只有1年。
SELECT PlotID, TreeID, Vigor, count(*) c
FROM tblTreeInfo
WHERE Vigor NOT IN (6, 8, 9)
GROUP BY PlotID, TreeID, Vigor
HAVING (c < 3)
我该如何获得同时满足Vigor不为6、8、9和c < 3的行?谢谢!
英文:
I'm trying to select trees from the following sample that have had the Vigor value 6,8 or 9 for the past three years straight. So, in the below grid I would want all rows with TreeID 2 and 3, but not any with TreeID 1.
PlotID ObsYear TreeID Vigor
MACFI0407 2020 1 8
MACFI0407 2021 1 8
MACFI0407 2022 1 8
MACFI0407 2020 2 1
MACFI0407 2021 2 1
MACFI0407 2022 2 8
MACFI0407 2020 3 1
MACFI0407 2021 3 1
MACFI0407 2022 3 1
This is what I thought I wanted, but it doesn't seem like mySQL is using the 'AND' to mean BOTH. This is leaving out TreeID 3 since it has a count of 3 (but Vigor value was 1)
SELECT PlotID, TreeID, Vigor, count(*) c
FROM tblTreeInfo
GROUP BY PlotID, TreeID, Vigor
HAVING (c < 3 AND Vigor NOT IN (6, 8, 9))
So I tried this, but this leaves out TreeID 2, since it has Vigor 8, but only for 1 year.
SELECT PlotID, TreeID, Vigor, count(*) c
FROM tblTreeInfo
WHERE Vigor NOT IN (6, 8, 9)
GROUP BY PlotID, TreeID, Vigor
HAVING (c < 3)
How can I get rows with both vigor not 6,8,9 AND c<3? Thank you!
答案1
得分: 0
以下是翻译好的代码部分:
你想要过去三年(2020年至2022年)的数据。首先要获取这些年份的数据行。然后,你想要排除这些年份中出现过6、8或9级别的树木ID。通过统计这些年份中出现这些级别的不同年份来找到这些ID,然后选择排除这些ID的数据行。
with rows_of_interest as
(
select *
from mytable
where obsyear between year(curdate()) - 3 and year(curdate()) - 1
)
select *
from rows_of_interest
where treeid not in
(
select treeid
from rows_of_interest
where vigor in (6, 8, 9)
group by treeid
having count(distinct obsyear) = 3
)
order by treeid, obsyear;
演示链接:https://dbfiddle.uk/DeRhqRoX
英文:
You want the last three years (2020 to 2022). Getting these rows is the first step. Then you want to omit tree IDs that had vigor 6, 8 or 9 in each of these years. Find these IDs, by counting distinct years in which these vigors occured. Then select the rows omitting these IDs.
with rows_of_interest as
(
select *
from mytable
where obsyear between year(curdate()) - 3 and year(curdate()) - 1
)
select *
from rows_of_interest
where treeid not in
(
select treeid
from rows_of_interest
where vigor in (6, 8, 9)
group by treeid
having count(distinct obsyear) = 3
)
order by treeid, obsyear;
答案2
得分: 0
以下是代码的翻译部分:
## 过去3年内生长不旺盛的树木
SELECT
T.*
FROM mytable AS T
LEFT JOIN (
SELECT treeid
FROM mytable
GROUP BY treeid
HAVING (MAX(CASE WHEN obsyear = year(curdate()) - 3 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 2 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 1 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)) = 3
) AS VIGOR ON T.TREEID = VIGOR.TREEID
WHERE VIGOR.TREEID IS NULL
## 过去3年内生长旺盛的树木
SELECT
T.*
FROM mytable AS T
INNER JOIN (
SELECT treeid
FROM mytable
GROUP BY treeid
HAVING (MAX(CASE WHEN obsyear = year(curdate()) - 3 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 2 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 1 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)) = 3
) AS VIGOR ON T.TREEID = VIGOR.TREEID
请注意,这些翻译仅包括代码部分,不包括问题或其他内容。
英文:
## TREES THAT ARE NOT VIGOROUS OVER PAST 3 YEARS
SELECT
T.*
FROM mytable AS T
LEFT JOIN (
SELECT treeid
FROM mytable
GROUP BY treeid
HAVING (MAX(CASE WHEN obsyear = year(curdate()) - 3 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 2 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 1 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)) = 3
) AS VIGOR ON T.TREEID = VIGOR.TREEID
WHERE VIGOR.TREEID IS NULL
plotid | obsyear | treeid | vigor |
---|---|---|---|
MACFI0407 | 2020 | 2 | 1 |
MACFI0407 | 2021 | 2 | 1 |
MACFI0407 | 2022 | 2 | 8 |
MACFI0407 | 2020 | 3 | 1 |
MACFI0407 | 2021 | 3 | 1 |
MACFI0407 | 2022 | 3 | 1 |
MACFI0407 | 2020 | 4 | 6 |
MACFI0407 | 2020 | 4 | 8 |
MACFI0407 | 2020 | 4 | 9 |
## TREES THAT ARE VIGOROUS OVER PAST 3 YEARS
SELECT
T.*
FROM mytable AS T
INNER JOIN (
SELECT treeid
FROM mytable
GROUP BY treeid
HAVING (MAX(CASE WHEN obsyear = year(curdate()) - 3 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 2 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)
+ MAX(CASE WHEN obsyear = year(curdate()) - 1 AND vigor IN (6,8,9) THEN 1 ELSE 0 END)) = 3
) AS VIGOR ON T.TREEID = VIGOR.TREEID
plotid | obsyear | treeid | vigor |
---|---|---|---|
MACFI0407 | 2020 | 1 | 8 |
MACFI0407 | 2021 | 1 | 8 |
MACFI0407 | 2022 | 1 | 8 |
MACFI0407 | 2020 | 5 | 6 |
MACFI0407 | 2021 | 5 | 8 |
MACFI0407 | 2022 | 5 | 9 |
MACFI0407 | 2023 | 5 | 9 |
MACFI0407 | 2020 | 5 | 6 |
MACFI0407 | 2020 | 5 | 8 |
MACFI0407 | 2022 | 5 | 9 |
MACFI0407 | 2022 | 5 | 9 |
答案3
得分: -1
一个简单的方法是包含 count(*) = 3
,因为只有最近3年具有 Vigor IN (6, 8, 9) 的树木必须被忽略:
with cte as (
SELECT PlotID, TreeID, Vigor, count(*) c
FROM mytable
GROUP BY PlotID, TreeID, Vigor
HAVING (c <= 3 AND Vigor NOT IN (6, 8, 9))
)
select t.*
from mytable t
inner join cte c on c.TreeID = t.TreeID
英文:
A Simple way to do it, is to include count(*) = 3
since only Trees with last 3 years having Vigor IN (6, 8, 9) must be ignored :
with cte as (
SELECT PlotID, TreeID, Vigor, count(*) c
FROM mytable
GROUP BY PlotID, TreeID, Vigor
HAVING (c <= 3 AND Vigor NOT IN (6, 8, 9))
)
select t.*
from mytable t
inner join cte c on c.TreeID = t.TreeID
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论