执行以下操作时出错:
SELECT *
from cGift c1 left join
(select c2.gidnumb, "t" new from cGift c2 where c2.gidnumb =
(select c3.gidnumb from cgift c3 where c3.id = c2.id and c3.date =
(select min(c4.date) from cgift c4 where c2.id == c4.id ))) tNew
on tNew.gidnumb = c1.gidnumb
基本上我有表格cgift,上面有捐款清单。我需要一个返回cgift的查询,其中包含一个包含“t”或null的额外列。每个捐赠者(cgift.id)的第一笔捐款(cgift.date)应为“t”,其余捐赠者为空。
示例:
gidnumb..id....date......new
10.......1.....2/1/2010..null
11.......2.....1/1/2010..t
12.......3.....1/1/2010..t
13.......1.....3/1/2010..null
14.......2.....2/1/2010..null
15.......4.....1/1/2010..t
16.......1.....1/1/2010..t
空值可以是blancs或f或wtvr。
任何人都可以告诉我我的查询有什么问题,这让我疯狂。
答案 0 :(得分:2)
我认为以下几乎适用于任何SQL产品:
SELECT
cGift.gidnumb,
cGift.id,
cGift.date,
first.isfirst
FROM cGift
LEFT JOIN (
SELECT id, MIN(date) AS date, 't' AS isfirst
FROM cGift
GROUP BY id
) first ON cGift.id = first.id AND cGift.date = first.date
更新(解决其他标准):
如果某人可能会在MIN(date)
上多次捐款,并且您只希望将一笔捐款标记为t
,则可以执行以下操作:
SELECT
cGift.gidnumb,
cGift.id,
cGift.date,
first.isfirst
FROM cGift
LEFT JOIN (
SELECT
MIN(g.gidnumb) AS g.gidnumb,
g.id,
g.date,
't' AS isfirst
FROM cGift g
INNER JOIN (
SELECT id, MIN(date) AS date
FROM cGift
GROUP BY id
) f ON g.id = f.id AND g.date = f.date
GROUP BY g.id, g.date
) first ON cGift.id = first.id AND cGift.date = first.date
也就是说,最里面的查询找到每个人的最小天数,就像之前的解决方案一样,但它也会详细列出具有特定gidnumb
值的列表,确保每个人只有一行匹配此列表在cGift
表中。
该查询仍应在任何DBMS中运行。考虑到双重分组,它可能效率较低。这是一个替代方案,它也只使用标准SQL,没有特定于供应商的功能(它应该比以前的查询更灵活):
SELECT
gidnumb,
id,
date,
CASE
WHEN NOT EXISTS (
SELECT *
FROM cGift
WHERE id = g.id
AND (
date < g.date OR
date = g.date AND gidnumb < g.gidnumb
)
)
THEN 't'
END AS isfirst
FROM cGift g
正如您所看到的,isfirst
列是使用表格上的自检来计算的:如果此表中没有相同id
的行和更早的日期,或者如果日期相同,较小的gidnumb
,此行应标记为t
。如果ELSE
没有CASE
部分,则隐含ELSE NULL
。如果您愿意,可以添加ELSE 'f'
。
尽管如此,您的SQL产品可能拥有可以通过构建可能更简单和更有效的查询而受益的功能。例如,有些产品支持排名功能(它们已经是SQL标准的一部分,只是它们还没有被普遍支持),而这就是你可以用名为ROW_NUMBER()
的排名函数做的事情:
SELECT
gidnumb,
id,
date,
CASE ROW_NUMBER() OVER (PARTITION BY id ORDER BY date, gidnumb)
WHEN 1
THEN 't'
END AS isfirst
FROM cGift g
此查询对按id
对其进行分区并按date
排序,然后按gidnumb
排序的行进行排名。在这种情况下排名为1
的每一行都应成为应与t
区分的行。
答案 1 :(得分:1)
我在查询中看不到错误。
但我建议您稍微修改一下您的查询。
这将返回每个身份的第一笔捐款
SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date);
这将返回下一次捐款
SELECT id, gidnymb, date, null AS new_column
FROM cGift
LEFT JOIN (
SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date)
) USING(id)
WHERE new_column IS NULL
现在使用UNION合并它。
(SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date))
UNION
(SELECT id, gidnymb, date, null AS new_column
FROM cGift
LEFT JOIN (
SELECT id, gidnymb, date, "t" As new_column
FROM cGift
GROUP BY id
HAVING date = MIN(date)
) USING(id)
WHERE new_column IS NULL)
我没有测试它,但你已经有了这个想法。
答案 2 :(得分:0)
在SQL Server 2005+中,您可以执行此操作
SELECT
gidnumb,
id,
Date,
CASE RANK() OVER (PARTITION BY id ORDER BY Date)
WHEN 1 Then 't'
ELSE Null
END
FROM
cGift