2011-04-07 53 views
3

我正在嘗試編寫一個查詢,該查詢將採用有限數量的歷史記錄並將結果顯示在一行中。使用表值函數返回列中的數據而不是行

例如,我有一桌子的人:

|PersonID|Forename|Surname 
|--------|--------|---------- 
|00000001|Andy |Cairns 
|00000002|John |Smith 

和所有的歷史地址表:

|PersonID|Date  |Street  |Town 
------------------------------------------- 
|00000001|2011-01-01|Main Street |MyTown 
|00000001|2010-01-01|Old Street |OldTown 
|00000002|2010-01-01|Diagon Alley |London 
|00000001|2009-01-01|First Street |OtherTown 

等。

我想退貨如下:

|PersonID|Name |MoveDate1 |Town1 |MoveDate2 |Town2 |MoveDate3 |Town3 
------------------------------------------------------------------------ 
|00000001|Andy |2011-01-01|MyTown|2010-01-01|OldTown|2009-01-01|OtherTown 
|00000002|John |2010-01-01|London|   |  |   | 

目前,我使用下面的查詢:

select PersonID, Name, s.mdate, s.town 
from dbo.people 
cross apply dbo.getAddressList as s 

和下面的表值函數:

alter function [dbo].[getAddressList] 
(
@personID 
) 
returns 
@addresslisttable 
(
    mdate smalldatetime 
    town char 
) 
as 
begin 
    insert into @addresslist (
    mdate 
    town 
) 
    select top 3 mdate, town 
    from dbo.addresses 
     where PersonID = @personID 
     order by mdate desc 
return 
end 

不幸的是,此函數返回一個新行每個地址,如下所示:

|PersonID|Name|MDate  |Town 
|00000001|Andy|2011-01-01|MyTown 
|00000001|Andy|2010-01-01|OldTown 
|00000001|Andy|2009-01-01|OtherTown 

如何返回字段中的每個返回的行?

在此先感謝。

+0

要明確,你只想返回三個城鎮是否正確?如果是這樣,您可以查看爲每個地址分配一個排名(1,2或3),然後使用該排名值運行一些插入語句,這些語句將填充結果表中的相應列。 – 2011-04-07 21:48:17

+0

添加了一個工作示例。這很粗糙,可能不是做事的最佳方式,但希望它至少能給你一些想法。 – 2011-04-07 22:11:00

回答

3

如果可能的話,你應該總是使用內聯TVFs優先於那些多語句。

ALTER FUNCTION [dbo].[getAddressList] 
( 
@personID INT 
) 
RETURNS TABLE 
AS 
RETURN 
(
WITH cte AS 
     (SELECT TOP 3 mdate, town, ROW_NUMBER() OVER (ORDER BY mdate DESC) rn 
     FROM dbo.addresses 
      WHERE PersonID = @personID 
      ORDER BY mdate DESC 
     ) 

    SELECT 
    MAX(CASE WHEN rn=1 THEN mdate END) AS MoveDate1, 
    MAX(CASE WHEN rn=1 THEN town END) AS Town1, 
    MAX(CASE WHEN rn=2 THEN mdate END) AS MoveDate2, 
    MAX(CASE WHEN rn=2 THEN town END) AS Town2, 
    MAX(CASE WHEN rn=3 THEN mdate END) AS MoveDate3, 
    MAX(CASE WHEN rn=3 THEN town END) AS Town3 
    FROM cte 
) 

我也會調查不使用TVF的相對性能。並做了上面JOINROW_NUMBER() OVER (PARTITION BY PersonID)PIVOT技術。

+0

太好了,非常感謝。這有很大幫助。 – 2011-04-09 15:57:29

0

這裏,檢查出來:

-- Create People (not like that... jeez...) 
CREATE TABLE #People (PersonID INT, Forename VARCHAR(25), Surname VARCHAR(25)) 
INSERT INTO #People VALUES (1, 'Andy', 'Cairns') 
INSERT INTO #People VALUES (2, 'John', 'Smith') 

-- Create historical addresses 
CREATE TABLE #Addy (PersonID INT, AddyDate DATETIME, Street VARCHAR(50), Town VARCHAR(50)) 
INSERT INTO #Addy VALUES (1, '2011-01-01', 'Main Street', 'MyTown') 
INSERT INTO #Addy VALUES (1, '2010-01-01', 'Old Street', 'OldTown') 
INSERT INTO #Addy VALUES (2, '2010-01-01', 'Diagon Alley', 'London') 
INSERT INTO #Addy VALUES (1, '2009-01-01', 'First Street', 'OtherTown') 

-- Create ranked addresses mapped to people 
SELECT p.Forename, p.Surname, a.*, 
     ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY p.PersonID) As Ordinal 
    INTO #Ranked 
FROM #People p INNER JOIN #Addy a ON p.PersonID = a.PersonID 

-- Make sure everything is kosher 
SELECT * FROM #People 
SELECT * FROM #Addy 
SELECT * FROM #Ranked 

-- Create a container for "final" results 
DECLARE @Results TABLE (PersonID INT, Forename VARCHAR(25) 
      , MoveDate1 DATETIME, Street1 VARCHAR(50), Town1 VARCHAR(50) 
      , MoveDate2 DATETIME, Street2 VARCHAR(50), Town2 VARCHAR(50) 
      , MoveDate3 DATETIME, Street3 VARCHAR(50), Town3 VARCHAR(50)) 

-- Get our people primed in the results table 
INSERT INTO @Results (PersonID, Forename) SELECT PersonID, Forename FROM #People 

-- Fill it up 
UPDATE @Results SET MoveDate1 = AddyDate, Street1 = Street, Town1 = Town FROM #Ranked INNER JOIN @Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 1 
UPDATE @Results SET MoveDate2 = AddyDate, Street2 = Street, Town2 = Town FROM #Ranked INNER JOIN @Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 2 
UPDATE @Results SET MoveDate3 = AddyDate, Street3 = Street, Town3 = Town FROM #Ranked INNER JOIN @Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 3 

-- Winsauce? 
SELECT * FROM @Results 

-- Cleanup 
DROP TABLE #People 
DROP TABLE #Addy 
DROP TABLE #Ranked 
+0

感謝您的幫助。 – 2011-04-09 15:57:47

相關問題