给出2个名为“table1”和“table1_hist”的表,结构上类似于:
TABLE1
id status date_this_status
1 open 2008-12-12
2 closed 2009-01-01
3 pending 2009-05-05
4 pending 2009-05-06
5 open 2009-06-01
TABLE1_hist
id status date_this_status
2 open 2008-12-24
2 pending 2008-12-26
3 open 2009-04-24
4 open 2009-05-04
如果table1是当前状态而table1_hist是table1的历史表,那么如何为每个具有最早日期的id返回行。换句话说,对于每个id,我需要知道它的最早状态和日期。
EXAMPLE:
For id 1 earliest status and date is open and 2008-12-12.
For id 2 earliest status and date is open and 2008-12-24.
我已经尝试过使用MIN(日期时间),联合,动态SQL等等。我今天刚刚到达了tsql编写器块并且卡住了。
编辑添加:呃。这是针对SQL2000数据库的,所以Alex Martelli的答案是行不通的。直到SQL2005才引入ROW_NUMBER。
答案 0 :(得分:6)
SQL Server 2005及更高版本支持SQL标准的有趣(相对较新)方面,“排名/窗口函数”,允许:
WITH AllRows AS (
SELECT id, status, date_this_status,
ROW_NUMBER() OVER(PARTITION BY id ORDER BY date_this_status ASC) AS row,
FROM (SELECT * FROM Table1 UNION SELECT * FROM Table1_hist) Both_tables
)
SELECT id, status, date_this_status
FROM AllRows
WHERE row = 1
ORDER BY id;
我还在使用nice(以及同样“new”)WITH
语法来避免在主SELECT
中嵌套子查询。
This article显示了如何在SQL Server中破解等效的ROW_NUMBER
(以及RANK
和DENSE_RANK
,另外两个“新”排名/窗口函数) 2000年 - 但这不一定很好,也不是特别好的,唉。
答案 1 :(得分:3)
以下代码示例完全自给自足,只需将其复制并粘贴到管理工作室查询中并点击F5 =)
DECLARE @TABLE1 TABLE
(
id INT,
status VARCHAR(50),
date_this_status DATETIME
)
DECLARE @TABLE1_hist TABLE
(
id INT,
status VARCHAR(50),
date_this_status DATETIME
)
--TABLE1
INSERT @TABLE1
SELECT 1, 'open', '2008-12-12' UNION ALL
SELECT 2, 'closed', '2009-01-01' UNION ALL
SELECT 3, 'pending', '2009-05-05' UNION ALL
SELECT 4, 'pending', '2009-05-06' UNION ALL
SELECT 5, 'open', '2009-06-01'
--TABLE1_hist
INSERT @TABLE1_hist
SELECT 2, 'open', '2008-12-24' UNION ALL
SELECT 2, 'pending', '2008-12-26' UNION ALL
SELECT 3, 'open', '2009-04-24' UNION ALL
SELECT 4, 'open', '2009-05-04'
SELECT x.id,
ISNULL(y.[status], x.[status]) AS [status],
ISNULL(y.date_this_status, x.date_this_status) AS date_this_status
FROM @TABLE1 x
LEFT JOIN (
SELECT a.*
FROM @TABLE1_hist a
INNER JOIN (
SELECT id,
MIN(date_this_status) AS date_this_status
FROM @TABLE1_hist
GROUP BY id
) b
ON a.id = b.id
AND a.date_this_status = b.date_this_status
) y
ON x.id = y.id
答案 2 :(得分:2)
SELECT id,
status,
date_this_status
FROM ( SELECT *
FROM Table1
UNION
SELECT *
from TABLE1_hist
) a
WHERE date_this_status = ( SELECT MIN(date_this_status)
FROM ( SELECT *
FROM Table1
UNION
SELECT *
from TABLE1_hist
) t
WHERE id = a.id
)
这有点难看,但似乎在MS SQL Server 2005中有效。
答案 3 :(得分:1)
您可以使用独占自我加入来完成此操作。加入历史表,然后再加入所有早期历史记录表。在where语句中,指定不允许存在任何先前的条目。
select t1.id,
isnull(hist.status, t1.status),
isnull(hist.date_this_status, t1.date_this_status)
from table1 t1
left join (
select h1.id, h1.status, h1.date_this_status
from table1_hist h1
left join table1_hist h2
on h2.id = h1.id
and h2.date_this_status < h1.date_this_status
where h2.date_this_status is null
) hist on hist.id = t1.id
有点心灵约束,但相当灵活和高效!
这假定没有两个具有完全相同日期的历史记录条目。如果有,请编写自联接,如:
left join table1_hist h2
on h2.id = h1.id
and (
h2.date_this_status < h1.date_this_status
or (h2.date_this_status = h1.date_this_status and h2.id < h1.id)
)
答案 4 :(得分:1)
如果我正确理解了OP,那么给定的ID可能会出现在TABLE1或TABLE1_HISTORY中,或者两者都出现。
在结果集中,您希望返回每个不同的ID以及与该ID关联的最旧状态/日期,而不管最旧的那个表恰好位于哪个表中。
因此,查看两个表并返回 表中没有记录的任何记录,因为它的ID date_this_status 。
试试这个:
SELECT ID, status, date_this_status FROM table1 ta WHERE
NOT EXISTS(SELECT null FROM table1 tb WHERE
tb.id = ta.id
AND tb.date_this_status < ta.date_this_status)
AND NOT EXISTS(SELECT null FROM table1_history tbh WHERE
tbh.id = ta.id
AND tbh.date_this_status < ta.date_this_status)
UNION ALL
SELECT ID, status, date_this_status FROM table1_history tah WHERE
NOT EXISTS(SELECT null FROM table1 tb WHERE
tb.id = tah.id
AND tb.date_this_status < tah.date_this_status)
AND NOT EXISTS(SELECT null FROM table1_history tbh WHERE
tbh.id = tah.id
AND tbh.date_this_status < tah.date_this_status)
这里有三个基本假设:
我们可以进行两项略微优化:
所以:
SELECT ID, status, date_this_status FROM table1 ta WHERE
NOT EXISTS(SELECT null FROM table1_history tbh WHERE
tbh.id = ta.id
)
UNION ALL
SELECT ID, status, date_this_status FROM table1_history tah WHERE
NOT EXISTS(SELECT null FROM table1_history tbh WHERE
tbh.id = tah.id
AND tbh.date_this_status < tah.date_this_status)
答案 5 :(得分:0)
如果这是表格的实际结构,则无法获得100%准确的答案,问题是对于任何给定记录,您可以为同一(最早)日期提供2种不同的状态,而您不会知道首先输入哪一个,因为历史表中没有主键
答案 6 :(得分:0)
暂时忽略“两个表”问题,我会使用以下逻辑......
SELECT
id, status, date
FROM
Table1_hist AS [data]
WHERE
[data].date = (SELECT MIN(date) FROM Table1_hist WHERE id = [data].id)
(编辑:根据BlackTigerX的评论,这假设没有id可以有多个具有相同日期时间的状态。)
将此推断为两个表的简单方法是使用breitak67的答案。将所有“my_table”实例替换为将两个表UNION在一起的子查询。这里的潜在问题是性能问题,因为您可能会发现索引变得无法使用。
加快这种做法的一种方法可能是使用隐含的知识:
1.主表总是有每个id的记录
2.历史表并不总是有记录
3.历史表中的任何记录总是比主表中的记录“旧”。
SELECT
[main].id,
ISNULL([hist].status, [main].status),
ISNULL([hist].date, [main].date)
FROM
Table1 AS [main]
LEFT JOIN
(
SELECT
id, status, date
FROM
Table1_hist AS [data]
WHERE
[data].date = (SELECT MIN(date) FROM Table1_hist WHERE id = [data].id)
)
AS [hist]
ON [hist].id = [main].id