[英]Comparing two dates in different tables SQL with criteria
點擊圖片查看表格結構和問題http://i.stack.imgur.com/odWKL.png
SELECT B.ENTITYID,
B.BALANCEDATE,
B.BALANCE,
MIN(DATEDIFF(DAY,B.BALANCEDATE,C.STATUSDATE)) RECENT
FROM BALANCES B JOIN STATUS C ON B.ENTITYID = C.ENTITYID
GROUP BY B.ENTITYID, B.BALANCEDATE,B.BALANCE
HAVING B.ENTITYID =1
我已經嘗試了以下方法,但是不能做得更好,因為更多的嵌套選擇在訪問相似屬性時存在問題:
如果您正在使用SQL Server,請參閱@ ljh的答案 。 以下解決方案適用於MySQL。 由於MySQL不支持CTE
和Window Function
所以有點亂。
SET @entity_name = 'ABCD';
SELECT b.*, d.Status
FROM Entity a
INNER JOIN Balances b
ON a.EntityID = b.EntityID
LEFT JOIN
(
SELECT a.EntityID,
a.StatusDate StartDate,
b.StatusDate + Interval -1 DAY EndDate,
a.Status
FROM
(
SELECT b.*, @r1 := @r1 + 1 AS Row_number
FROM `Entity` a
INNER JOIN Status b
ON a.EntityID = b.EntityID
CROSS JOIN (SELECT @r1 := 0) rowCount
WHERE a.EntityName = @entity_name
ORDER BY b.Status ASC
) a
LEFT JOIN
(
SELECT b.*, @r2 := @r2 + 1 AS Row_number
FROM `Entity` a
INNER JOIN Status b
ON a.EntityID = b.EntityID
CROSS JOIN (SELECT @r2 := 1) rowCount
WHERE a.EntityName = @entity_name
ORDER BY b.Status ASC
) b ON a.Row_number = b.Row_number
) d
ON b.BalanceDate BETWEEN d.StartDate AND d.EndDate
WHERE a.EntityName = @entity_name
由於MySQL不支持窗口函數,例如ROW_NUMBER()
,因此下面的查詢使用User Variable
為每條記錄提供類似於ROW_NUMBER()
行號,然后用於在其他子查詢上加入。
SELECT b.*, @r1 := @r1 + 1 AS Row_number
FROM `Entity` a
INNER JOIN Status b
ON a.EntityID = b.EntityID
CROSS JOIN (SELECT @r1 := 0) rowCount
WHERE a.EntityName = @entity_name
ORDER BY b.Status ASC
OUTPUT
╔══════════╦═════════════════════════════════╦════════╦════════════╗
║ ENTITYID ║ STATUSDATE ║ STATUS ║ ROW_NUMBER ║
╠══════════╬═════════════════════════════════╬════════╬════════════╣
║ 1 ║ May, 29 2010 00:00:00+0000 ║ A ║ 1 ║
║ 1 ║ April, 16 2010 00:00:00+0000 ║ B ║ 2 ║
║ 1 ║ April, 02 2010 00:00:00+0000 ║ C ║ 3 ║
║ 1 ║ February, 26 2010 00:00:00+0000 ║ D ║ 4 ║
╚══════════╩═════════════════════════════════╩════════╩════════════╝
為記錄提供行號的主要目的是它將用於連接另一個子查詢,以便我們可以為每個Status
獲取StartDate
和EndDate
。 這在SQL Server 2012
上很容易,因為它有一個名為LAG()的窗口函數
╔══════════╦═════════════════════════════════╦══════════════════════════════╦════════╗
║ ENTITYID ║ STARTDATE ║ ENDDATE ║ STATUS ║
╠══════════╬═════════════════════════════════╬══════════════════════════════╬════════╣
║ 1 ║ May, 29 2010 00:00:00+0000 ║ (null) ║ A ║
║ 1 ║ April, 16 2010 00:00:00+0000 ║ May, 28 2010 00:00:00+0000 ║ B ║
║ 1 ║ April, 02 2010 00:00:00+0000 ║ April, 15 2010 00:00:00+0000 ║ C ║
║ 1 ║ February, 26 2010 00:00:00+0000 ║ April, 01 2010 00:00:00+0000 ║ D ║
╚══════════╩═════════════════════════════════╩══════════════════════════════╩════════╝
狀態范圍組織完成后。 現在,它已成為每個“ Balances
的查閱狀態的基礎。
最終結果
╔══════════╦═════════════════════════════════╦═════════╦════════╗
║ ENTITYID ║ BALANCEDATE ║ BALANCE ║ STATUS ║
╠══════════╬═════════════════════════════════╬═════════╬════════╣
║ 1 ║ May, 01 2010 00:00:00+0000 ║ 100 ║ B ║
║ 1 ║ April, 01 2010 00:00:00+0000 ║ 50 ║ D ║
║ 1 ║ March, 01 2010 00:00:00+0000 ║ 75 ║ D ║
║ 1 ║ February, 01 2010 00:00:00+0000 ║ 85 ║ (null) ║
╚══════════╩═════════════════════════════════╩═════════╩════════╝
MySQL
演示的上述查詢可以通過使用Common Table Expression
和使用LAG()的Window Function
在TSQL
輕松轉換( 僅在SQL Server 2012中引入 )
WITH lookupTable
AS
(
SELECT EntityID,
StatusDate StartDate,
DATEADD(DAY, -1, LAG(StatusDate) OVER(PARTITION BY EntityID ORDER BY Status)) EndDate,
Status
FROM Status
)
SELECT b.*, d.Status
FROM Entity a
INNER JOIN Balances b
ON a.EntityID = b.EntityID
LEFT JOIN lookupTable d
ON b.BalanceDate BETWEEN d.StartDate AND d.EndDate AND
d.EntityID = a.EntityID
WHERE a.EntityName = 'ABCD'
OUTPUT
╔══════════╦═════════════════════════════════╦═════════╦════════╗
║ ENTITYID ║ BALANCEDATE ║ BALANCE ║ STATUS ║
╠══════════╬═════════════════════════════════╬═════════╬════════╣
║ 1 ║ May, 01 2010 00:00:00+0000 ║ 100 ║ B ║
║ 1 ║ April, 01 2010 00:00:00+0000 ║ 50 ║ D ║
║ 1 ║ March, 01 2010 00:00:00+0000 ║ 75 ║ D ║
║ 1 ║ February, 01 2010 00:00:00+0000 ║ 85 ║ (null) ║
╚══════════╩═════════════════════════════════╩═════════╩════════╝
以SQL Server 2012為RDBMS為例。 這是您需要的第一個查詢。
這個答案使用了SQL CTE(公用表表達式),它可能不適用於其他RDBMS系統。
SQL FIDDLE DEMO
查詢說明:
1.首先連接[Balances]和[Status]表,使用BalanceDate> StatusDate過濾出結果,從這兩個表中返回所有列,這是因為以后需要使用所有列。
2.使用[Entity]表加入Step .1的輸出,使用EntityName過濾掉結果,仍然保留3個表中的所有列,當然不需要重復EntityID。
3.使用CTE保存連接
4.使用CTE保存應用於聯接輸出的等級
5.使用等級編號按BalanceDate過濾結果和訂單
;with CTE_AfterJoin
as
(
select E.EntityID, E.EnityName, C.BalanceDate, C.Balance, C.StatusDate, C.status
from Entity E
left join (
select B.EntityID, B.BalanceDate, B.Balance,S.StatusDate, S.[Status]
from Balances B
left join [Status] S
on B.EntityID = S.EntityID and B.BalanceDate > S.StatusDate
) C
on E.EntityID = C.EntityID
where E.EnityName = 'ABCD'
),
CTE_afterRank
as
(
select EnityName, BalanceDate, Balance,
rank() over (partition by BalanceDate order by StatusDate desc) as Rn, Status
from CTE_AfterJoin
)
select EnityName, BalanceDate, Balance, Status
from CTE_afterRank
where Rn = 1
order by BalanceDate desc
同樣在SQLServer2005 +中,您可以使用APPLY()運算符選項。
APPLY運算符允許您連接兩個表表達式。 每次對左表表達式中的每一行都處理右表表達式。最終結果集包含從左表表達式中選擇的所有列,然后是右表表達式的所有列。 對於右表表達式中沒有相應匹配項的行,OUTER APLLY在右表表達式的列中包含NULL值。
SELECT e.EntityName, b.BalanceDate AS Date, b.Balance, o.Status
FROM Entity e JOIN Balances b ON e.EntityID = b.EntityID
OUTER APPLY (
SELECT TOP 1 s.Status AS Status
FROM Status s
WHERE b.EntityID = s.EntityID
AND s.StatusDate < b.BalanceDate
ORDER BY s.StatusDate DESC
) o
WHERE e.EntityName = 'ABCD'
為了提高性能(強制INDEX SEEK操作),請將該索引與INCLUDE子句一起使用.INCLUDE子句將數據添加到最低/葉級別而不是索引樹中。 這使得索引更小,因為它不是樹的一部分
CREATE INDEX x ON Status(StatusDate) INCLUDE(EntityID, Status)
CREATE INDEX x ON Entity(EntityName) INCLUDE(EntityID)
CREATE INDEX x ON Balances(EntityID, BalanceDate, Balance)
在SQLFiddle上演示
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.