当列名基于变量结果时,如何 return 来自多个列的值

How do I return values from multiple columns when the column names are based on a variable result

我在 SQL 服务器中有一个 table,它在单独的列中列出旅程中不同阶段的日期。我还有一个专栏,显示当前旅程的阶段。

对于每个阶段,都有一列 header 表示阶段名称,列中将是与该阶段关联的日期。

我想要 运行 一个 SELECT 语句,它将 'current stage' 与列名和 return 相关联的日期相匹配。

例如,这可能是 table(实际的 table 有 40 个阶段 - 不要问!):

record id | current stage      | met client | contract agreed | service completed | on hold**
   11111S | met client         | 2019-01-02 | NULL            | NULL              | NULL
   22222P | contract agreed    | 2019-01-02 | 2019-01-20      | NULL              | NULL
   33333A | on hold            | 2019-01-02 | 2019-01-20      | NULL              | 2019-02-10
   44444C | service completed  | 2019-01-02 | 2019-01-20      | 2019-03-01        | 2019-02-10

我想写一个 SELECT 语句来显示:

record_id | current_stage     | date_of_current_stage
   11111S | met client        | 2019-01-02 |
   22222P | contract agreed   | 2019-01-20 |
   33333A | on hold           | 2019-02-10 |
   44444C | service completed | 2019-03-01 |

您可以使用动态 SQL 实现它,方法如下(将 #X 替换为您的实际 table):

CREATE TABLE #X
(
    [record id] int
    , [current stage] varchar(255)
    , [met client] date
    , [contract agreed] date
    , [service completed] date
    , [on hold] date
)

INSERT INTO #X VALUES (11111, 'met client', '2019-01-02', NULL, NULL, NULL)
INSERT INTO #X VALUES (22222, 'contract agreed', '2019-01-02', '2019-01-20', NULL, NULL)
INSERT INTO #X VALUES (33333, 'on hold', '2019-01-02', '2019-01-20', NULL, '2019-02-10')
INSERT INTO #X VALUES (44444, 'service completed', '2019-01-02', '2019-01-20', '2019-03-01', '2019-02-10')

DECLARE @y table
(
    [record id] int
    , [statement] varchar(1000)
)

INSERT INTO @y
(
    [record id]
    , [statement]
)

SELECT
    [record id]
    , 'SELECT [record id], [current stage], ' + QUOTENAME([current stage]) + ' [date of current stage] FROM #X WHERE [record id] = ' + CAST([record id] AS varchar(20)) [statement]
FROM #X

DECLARE
    @CurrentRecord int
    , @SQL nvarchar(max) = ''

WHILE EXISTS (SELECT * FROM @y)
BEGIN

    SELECT @CurrentRecord = MIN([record id]) FROM @y

    IF (SELECT COUNT(*) FROM @y) = 1
        SET @SQL = @SQL + (SELECT [statement] FROM @y WHERE [record id] = @CurrentRecord)
    ELSE
        SET @SQL = @SQL + (SELECT [statement] + ' UNION ALL ' FROM @y WHERE [record id] = @CurrentRecord)

    DELETE @y
    WHERE [record id] = @CurrentRecord

END

EXEC sp_executesql @SQL

DROP TABLE #X

您可以使用 UNION ALL

的子查询
select record_id, 
    current_stage, 
    max(met_client) as date_of_current_stage
from (
    select record_id, current_stage, met_client from test union all
    select record_id, current_stage, contract_agreed from test union all
    select record_id, current_stage, service_completed from test union all
    select record_id, current_stage, on_hold from test) t group by record_id, current_stage

如果你想让它动态,那么你可以使用动态 UNPIVOT,如下所示

DECLARE @col VARCHAR(1000)
DECLARE @sql VARCHAR(2000)

SELECT @col = COALESCE(@col + ', ','') + QUOTENAME([current stage])
FROM #Test

SELECT @col

SET @sql = '
        SELECT t.[record id], [current stage], date_of_current_stage
        FROM #Test t
        LEFT JOIN (SELECT [record id], Stage, Dates
            FROM (
                SELECT * FROM #Test
            ) p
            UNPIVOT ( 
                Dates For Stage IN (' + @col + ') 
            ) pv
        ) s ON t.[record id] = s.[record id]
            AND t.[current stage] = s.Stage '

EXEC(@sql)

您似乎只需要匹配 current_stage 列的相应列值。您可以尝试以下查询 -

SELECT record_id
      ,current_stage
      ,CASE WHEN current_stage = 'met client' THEN [met client]
            WHEN current_stage = 'contract agreed' THEN [contract agreed]
            WHEN current_stage = 'service completed' THEN [service completed]
            WHEN current_stage = 'on hold' THEN [on hold] END date_of_current_stage
FROM YOUR_TABLE;

如果这有其他逻辑,this文章可能对您有所帮助。

使用UNPIVOT,这样current stage值必须满足列名:

CREATE TABLE #Test  (
    [record id]         INT,
    [current stage]     VARCHAR(32),
    [met client]        DATE,
    [contract agreed]   DATE,
    [service completed] DATE,
    [on hold]           DATE
)

INSERT #Test ([record id],
    [current stage],
    [met client],
    [contract agreed],
    [service completed],
    [on hold])
VALUES
    (11111, 'met client', '2019-01-02', NULL, NULL, NULL),
    (22222, 'contract agreed', '2019-01-02', '2019-01-20', NULL, NULL),
    (33333, 'on hold', '2019-01-02', '2019-01-20', NULL, '2019-02-10'),
    (44444, 'service completed', '2019-01-02', '2019-01-20', '2019-03-01', '2019-02-10')

DECLARE @SQL    NVARCHAR(MAX),
    @ColumnsList    NVARCHAR(MAX) = ''

SELECT @ColumnsList = STUFF (
    (
        SELECT DISTINCT ',' + QUOTENAME([current stage])
        FROM #Test
        FOR XML PATH('')
    ),
    1, 1, '')

SET @SQL = 
    '
    SELECT t.[record id], [current stage], Dates
    FROM #Test t
        LEFT JOIN (
        SELECT [record id], Stage, Dates
        FROM (
            SELECT [record id],' + @ColumnsList + '
            FROM #Test
        ) p
        UNPIVOT ( 
            Dates For Stage IN (' + @ColumnsList + ') 
        ) pv
    ) s ON t.[record id] = s.[record id]
        AND t.[current stage] = s.Stage
'

EXEC sp_executesql @SQL

已更新 以避免必须手动指定每个阶段

以下使用动态 UNPIVOT 操作的查询将完成工作:

CREATE TABLE #yourTable ( [record id] INT,[current stage] VARCHAR(255), [met client] DATE, [contract agreed] DATE, [service completed] DATE, [on hold] DATE)

INSERT INTO #yourTable VALUES 
(11111, 'met client', '2019-01-02', NULL, NULL, NULL),
(22222, 'contract agreed', '2019-01-02', '2019-01-20', NULL, NULL),
(33333, 'on hold', '2019-01-02', '2019-01-20', NULL, '2019-02-10'),
(44444, 'service completed', '2019-01-02', '2019-01-20', '2019-03-01', '2019-02-10')


DECLARE @col NVARCHAR(MAX) = '';
SELECT @col += ',' + QUOTENAME([current stage]) FROM #yourTable
SET @col = STUFF(@col,1,1,'')

EXEC ( 'SELECT unpiv.[record id], unpiv.[current stage], [Date] AS [Date_of_current_stage] FROM #yourTable UNPIVOT ([Date] FOR [Stage] IN ('+@col+') ) unpiv WHERE [current stage] = [Stage]')