Sql Server Self JOIN (pushing column values down)

那年仲夏 提交于 2019-12-11 19:19:26

问题


I am asked to do the following: "CycleStartDate needs to be the BillDate from the previous BillDate record. If a previous record does not exist, you should use the most recent CycleEndDate from the DataTime table"

CycleStartDate and CycleEndDate are columns in a table called DataTime

BillDate is a column in a table called BillingData

This is the BillDate values:

2012-07-27 00:00:00.000
2012-07-27 00:00:00.000
2012-08-27 00:00:00.000
2012-08-27 00:00:00.000
2012-09-28 00:00:00.000
2012-09-28 00:00:00.000
2012-10-26 00:00:00.000
2012-10-26 00:00:00.000
2012-11-27 00:00:00.000
2012-11-27 00:00:00.000
2012-12-27 00:00:00.000

How would I set the CycleStartDate values based on the requirements? The tables Datetime and BillingData are connected by a column called MeterID.


回答1:


Try something similar to this...

SELECT B.BillDate, 
       ISNULL(
              B2.BillDate, 
              (SELECT MAX(CycleEndDate) FROM DataTime DT WHERE DT.MeterID = B.MeterID)
             )  CycleStartDate 
    FROM BillingData B
    OUTER APPLY (
                 SELECT TOP 1 B2.BillDate 
                        FROM BillingData B2 
                        WHERE B2.MeterID = B.MeterID AND 
                              B2.BillingData < B.BillingData 
                        ORDER BY B2.BillingData DESC
                ) B2

I still have one doubt... Do you need to take the SELECT MAX(CycleEndDate) FROM DataTime DT WHERE DT.MeterID = B.MeterID or the SELECT MAX(CycleEndDate) FROM DataTime DT WHERE DT.MeterID = B.MeterID AND DT.CycleEndDate < B.BillDate?

But it can be done without the OUTER APPLY...

SELECT B.BillDate, 
       ISNULL(
              (SELECT MAX(B2.BillDate) 
                      FROM BillingData B2 
                      WHERE B2.MeterID = B.MeterID AND 
                            B2.BillingData < B.BillingData),

              (SELECT MAX(CycleEndDate) FROM DataTime DT WHERE DT.MeterID = B.MeterID)
             )  CycleStartDate 
       FROM BillingData B

I think the second version is quite readable... For each row of BillingData B, look for the biggest BillDate (MAX(B2.BillDate)) lesser than the current BillDate and of the same MeterID. If not present (the ISNULL, if the first one is not present then it's NULL, so it goes to the second part of the ISNULL), look for the biggest CycleEndDate from DataTime with the same MeterID and return it.




回答2:


You can use the ROW_NUMBER() function for offsetting a JOIN:

SELECT a.BillDate, COALESCE(b.BillDate,c.CycleEndDate) 'CycleEndDate'
FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY MeterID ORDER BY BillDate DESC)'RowRank'
      FROM YourTable
     )a
LEFT JOIN (SELECT *,ROW_NUMBER() OVER (PARTITION BY MeterID ORDER BY BillDate DESC)'RowRank'
           FROM YourTable
           )b
      ON a.RowRank = b.RowRank - 1
      AND a.MeterID = b.MeterID
LEFT JOIN (SELECT MeterID,MAX(CycleEndDate)'CycleEndDate'
           FROM DataTime
           GROUP BY MeterID
          ) c
    ON a.MeterID = c.MeterID

The PARTITION BY may not be necessary as well as the MeterID criteria in the JOIN, your wording is a little confusing as to whether the ORDER BY should be ascending or descending, as it is above the newest record will be the one that gets it's date from the DateTime table, remove DESC to make it the oldest record that gets it's value from that table.



来源:https://stackoverflow.com/questions/18193104/sql-server-self-join-pushing-column-values-down

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!