2017-10-05 62 views
1

我想出了一個非常簡單的示例來演示我試圖解決的問題。根據同一表中的其他行選擇行

,我需要選擇客戶合同是...

1)已到期或將在未來三個月

2)沒有新的合同已經到期中地點。
全部過期合同與新合同一起保留在表格中。

根據這些業務規則,測試數據的預期結果是返回合同ID 6(客戶3),因爲它是沒有新合同和合同ID 7(客戶4)的過期合同,因爲它具有小於3個月運行。

我看過一些例子,其中解決方案是將表加入自己

例如, how do I query sql for a latest record date for each user

我想我可以爲每個客戶選擇最近的合同,然後檢查它的到期日期,但它只返回合同ID 6而不是像我期望的那樣7。我正在使用SQL 2008 R2。

任何想法,我要錯了嗎?

SELECT [ContractID] 
     ,[StartDate] 
     ,[ExpiryDate] 
     ,TC.[CustomerID] 
    FROM [Test].[dbo].[TestContract] TC 
    inner join 
    (
    select CustomerID, 
    MAX(ExpiryDate) as MaxDate 
    From Test.dbo.TestContract 
    Group by CustomerID  
)CM on TC.CustomerID = CM.CustomerID and TC.ExpiryDate = CM.MaxDate 
    Where TC.ExpiryDate < DateAdd(DAY, 30, GETDATE()) 

這裏是我的測試數據

ContractID StartDate     ExpiryDate    CustomerID 
1   2017-02-01 00:00:00.000 2018-02-01 00:00:00.000 1 
2   2016-01-01 00:00:00.000 2017-01-01 00:00:00.000 1 
4   2016-01-01 00:00:00.000 2017-11-01 00:00:00.000 2 
5   2017-11-01 00:00:00.000 2018-11-01 00:00:00.000 2 
6   2016-10-01 00:00:00.000 2017-10-01 00:00:00.000 3 
7   2016-12-01 00:00:00.000 2017-12-01 00:00:00.000 4 
8   2015-12-01 00:00:00.000 2016-12-01 00:00:00.000 4 
9   2017-06-01 00:00:00.000 2018-06-01 00:00:00.000 5 

這裏是一個腳本來重新創建我的測試表和數據。

USE [Test] 
GO 
/****** Object: Table [dbo].[TestContract] Script Date: 10/05/2017 17:07:33 ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[TestContract](
    [ContractID] [int] IDENTITY(1,1) NOT NULL, 
    [StartDate] [datetime] NOT NULL, 
    [ExpiryDate] [datetime] NOT NULL, 
    [CustomerID] [int] NOT NULL, 
CONSTRAINT [PK_TestContract] PRIMARY KEY CLUSTERED 
(
    [ContractID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET IDENTITY_INSERT [dbo].[TestContract] ON 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (1, CAST(0x0000A70D00000000 AS DateTime), CAST(0x0000A87A00000000 AS DateTime), 1) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (2, CAST(0x0000A58000000000 AS DateTime), CAST(0x0000A6EE00000000 AS DateTime), 1) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (4, CAST(0x0000A58000000000 AS DateTime), CAST(0x0000A81E00000000 AS DateTime), 2) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (5, CAST(0x0000A81E00000000 AS DateTime), CAST(0x0000A98B00000000 AS DateTime), 2) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (6, CAST(0x0000A69200000000 AS DateTime), CAST(0x0000A7FF00000000 AS DateTime), 3) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (7, CAST(0x0000A6CF00000000 AS DateTime), CAST(0x0000A83C00000000 AS DateTime), 4) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (8, CAST(0x0000A56100000000 AS DateTime), CAST(0x0000A6CF00000000 AS DateTime), 4) 
INSERT [dbo].[TestContract] ([ContractID], [StartDate], [ExpiryDate], [CustomerID]) VALUES (9, CAST(0x0000A78500000000 AS DateTime), CAST(0x0000A8F200000000 AS DateTime), 5) 
SET IDENTITY_INSERT [dbo].[TestContract] OFF 
+0

什麼是您的預期輸出 – TheGameiswar

+0

@TheGameiswar它在這個問題寫有:) ID 6和7 –

+1

Anagha拿了我的愚蠢的錯誤。 30應該是90.我會去測試它,現在我的真實數據顯然會變得更加複雜。感謝大家提出的解決方案。我只需要一個工作:)我會看看我是否能夠理解你在描述什麼,因爲我確信在那裏有很好的學習機會。 。 –

回答

2

你說「到期或將在未來三個月內到期」是指應該有狀態像DateAdd(DAY, 90, GETDATE())而不是30

在您的查詢修改後:

SELECT [ContractID] 
     ,[StartDate] 
     ,[ExpiryDate] 
     ,TC.[CustomerID] 
    FROM [TestContract] TC 
    inner join 
    (
    select CustomerID, 
    MAX(ExpiryDate) as MaxDate 
    From TestContract 
    Group by CustomerID  
)CM on TC.CustomerID = CM.CustomerID and TC.ExpiryDate = CM.MaxDate 
    Where TC.ExpiryDate < DateAdd(DAY, 90, GETDATE()) 

一個替代方案:

select [ContractID],[StartDate],[ExpiryDate] ,[CustomerID] 
from (select [ContractID],[StartDate],ExpiryDate ,TC.[CustomerID], 
     ROW_NUMBER() over (partition by customerid order by ExpiryDate desc) rn 
FROM [TestContract] TC) a 
where rn =1 
and ExpiryDate < DateAdd(DAY, 90, GETDATE()) 
+1

你選了它:)我不相信我錯過了它。我非常確信我的解決方案不起作用,我甚至沒有找到愚蠢的錯誤。我只是將我的30改爲90,我就得到了預期的結果。 –

+0

有時候會發生:) – Anagha

1

使用ROW_NUMBER()在子查詢中獲得爲每一位客戶的最新合同,然後再檢查expirydate

SELECT ContractID,StartDate,ExpiryDate,CustomerID 
FROM (
     SELECT ContractID,StartDate,ExpiryDate,CustomerID, 
       ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY ExpiryDate DESC) AS RN 
     FROM [YourTable] 
) X 
WHERE X.RN=1 AND X.ExpiryDate < DateAdd(DAY, 90, GETDATE()) 
+1

謝謝,這也有效,但我原來的解決方案非常接近工作,不是很漂亮。我先給發現我的錯誤的人打了個盹。我會看到哪個更容易實現。 –

2

我理解這個問題的方式,像ROW_NUMBER窗口函數()不應該neccessary。

使用JOIN方法...

SELECT 
    TC.ContractID 
    , TC.StartDate 
    , TC.ExpiryDate 
    , TC.CustomerID 
FROM dbo.TestContract TC 
LEFT JOIN dbo.TestContract TC2 
    ON TC2.CustomerID = TC.CustomerID 
    AND TC2.StartDate > TC.StartDate 
WHERE 
    TC.ExpiryDate < dateadd(day, 90, getdate()) 
    AND TC2.ContractID is null 

然而,由於我們不需要在SELECT子句中,從連接表的任何列,我反而選擇使用NOT EXISTS。雖然這兩者都不是很複雜,但我更容易推理,因爲在閱讀時它更接近業務規則,並且不需要考慮確定聯接是否會生成其他行......

SELECT 
    TC.ContractID 
    , TC.StartDate 
    , TC.ExpiryDate 
    , TC.CustomerID 
FROM dbo.TestContract TC 
WHERE 
    TC.ExpiryDate < dateadd(day, 90, getdate()) 
    AND NOT EXISTS (
     SELECT * 
     FROM dbo.TestContract TC2 
     WHERE 
      TC2.CustomerID = TC.CustomerID 
      AND TC2.StartDate > TC.StartDate 
     ) 

當你有內部連接的表,僅僅在其加入的條件下使用,或外部連接表,僅僅在自己使用的連接條件,在where子句中,你經常可以重新編寫與查詢存在或不存在。

+0

謝謝傑夫,這兩個很好的答案,比我想出的更簡單。 我想我會嘗試並實現你的第二個答案,因爲不需要包含外連接。 –

+0

不客氣,謝謝你回覆評論。很確定這些解決方案的性能比行號方法更好。但是,如果您沒有太多數據,查看查詢計劃時可能只會引起注意。 –

0
SELECT ContractID,StartDate,ExpiryDate,CustomerID 
FROM (
     SELECT ContractID,StartDate,ExpiryDate,CustomerID, 
       ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY ExpiryDate DESC) AS RN 
     FROM TestContract 
) a 
WHERE a.RN=1 AND (a.ExpiryDate < DateAdd(DAY, 30, GETDATE()) OR a.ExpiryDate <= DateAdd(M, 3, GETDATE())) 
相關問題