使用表值函数以列而不是行返回数据
Posted
技术标签:
【中文标题】使用表值函数以列而不是行返回数据【英文标题】:Use a table-value function to return data in columns instead of rows 【发布时间】:2011-04-07 21:42:16 【问题描述】:我正在尝试编写一个查询,它将获取有限数量的历史记录并在一行中显示结果。
例如,我有一张人桌:
|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
如何在字段中返回每个返回的行?
提前致谢。
【问题讨论】:
明确一点,您只想返回最多三个城镇对吗?如果是这样,您可以考虑为每个地址分配一个排名(1、2 或 3),然后使用该排名值运行一些插入语句,这些语句将填充结果表中的适当列。 添加了一个工作示例。这很粗糙,可能不是最好的做事方式,但希望它至少能给你一些想法。 【参考方案1】:在可能的情况下,您应该始终使用内联 TVF 而不是多语句的。
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 的相对性能。并执行上述JOIN
、ROW_NUMBER() OVER (PARTITION BY PersonID)
和PIVOT
技术。
【讨论】:
【参考方案2】:在这里,检查一下:
-- 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
【讨论】:
以上是关于使用表值函数以列而不是行返回数据的主要内容,如果未能解决你的问题,请参考以下文章