Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 使用表值函数以列而不是行的形式返回数据_Sql Server_User Defined Functions - Fatal编程技术网

Sql server 使用表值函数以列而不是行的形式返回数据

Sql server 使用表值函数以列而不是行的形式返回数据,sql-server,user-defined-functions,Sql Server,User Defined Functions,我正在尝试编写一个查询,它将获取有限数量的历史记录,并将结果显示在一行中 例如,我有一张人员表: |PersonID|Forename|Surname |--------|--------|---------- |00000001|Andy |Cairns |00000002|John |Smith 以及所有历史地址的表格: |PersonID|Date |Street |Town ---------------------------------------

我正在尝试编写一个查询,它将获取有限数量的历史记录,并将结果显示在一行中

例如,我有一张人员表:

|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
如何返回字段中返回的每一行

提前感谢。

在这里,请查看:

-- 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

在可能的情况下,您应该始终优先使用内联TVF而不是多状态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()(按PersonID划分)
和上面的
PIVOT
技术。

请澄清,您最多只想返回三个城镇,对吗?如果是这样,您可以考虑为每个地址分配一个秩(1、2或3),然后使用该秩值运行一些insert语句,以填充结果表中的相应列。添加了一个工作示例。这很粗糙,可能不是做事情的最佳方式,但希望它至少能给你一些想法。