【问题标题】:Dynamic Pivot using Temp tables使用临时表的动态数据透视
【发布时间】:2021-10-23 08:47:11
【问题描述】:

我有三个查询,它们都在构建三个单独的表。程序、DRG 和诊断。我不知道患者可能有多少程序、有效的 DRG 或诊断。但是我只需要某些程序代码、DRG 和诊断代码。我将每个条目都排在临时表中。现在让我们只使用一张桌子,因为其他两张桌子的答案是一样的。让我们使用诊断表。

在诊断表中,我有一个 Rank 字段,它将在 Dx_Rank 字段中输入 Dx_1、Dx_2、Dx_3,这样我就可以将它们拉出来,但我不知道会有多少 Dx。我需要这些都在一条线上。所以输出会是这样的。

Patient_Account、Visit_Key、Dx_1、Dx_1_Description、Dx_2、Dx_2_Dexcription、Dx_3、Dx_3_Description

从一个看起来像这样的表

> Patient_Account, Visit_Key, Dx_Code, Dx_Priority, Dx_Rank, Dx_Desctiption  
> 123456789, #PAS203234, J20, 3, Dx_1, Left Hip is Broken 
> 123456789, #PAS203234, A32, 6, Dx_2, Left Knee is broken 
> 123456789, #PAS203234, R4786, 8, Dx_3, Left Ankle is broken 
> 987654321, #PAS435678, DF346, 2, Dx_1, Right Arm is broken  
> 987654321, #PAS435678, DT342.12, 4, Dx_2, Right Wrist is broken

所以这里我有两个患者,输出应该是这样的。

Patient_Account, Visit_Key, Dx_1, Dx_1_Description, Dx_2, Dx_2_Description, Dx_3, Dx_3_Description

123456789, #PAS203234, J20, Left Hip is broken, A32, Left Knee is broken, R4786, Left Ankle is broken
987654321, #PAS435678, DF346, Right Arm is broken, DT342.12, Right Wrist is broken, , ,

所以我想我会做一个 PIVOT。我设置了所有东西,但它并没有给我想要的东西。我在右列中得到 Dx_1,但是当有 Dx_2 时,它在右列中,但它位于单独的行上。我希望这一切都在一行上,因为最终输出将进入 Excel,我想让它动态化,所以如果我得到的最大诊断是 Dx_3,它将停止并且没有 7 个额外的空白列。如果我将其硬编码为每个有 10 个,然后当我最终得到一个有 11 个的患者并且我错过了一个,因为我的诊断限制为 10 时会发生什么。

这是我目前的脚本。

IF OBJECT_ID('tempdb..#PROCEDURES_CPT') IS NOT NULL 
    DROP TABLE #PROCEDURES_CPT
IF OBJECT_ID('tempdb..#PROCEDURES_CPT_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURE_CPT_PIVOT

IF OBJECT_ID('tempdb..#PROCEDURES_DRG') IS NOT NULL 
DROP TABLE #PROCEDURES_DRG
IF OBJECT_ID('tempdb..#PROCEDURES_DRG_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURES_DRG_PIVOT

IF OBJECT_ID('tempdb..#PROCEDURES_DX') IS NOT NULL 
DROP TABLE #PROCEDURES_DX
IF OBJECT_ID('tempdb..#PROCEDURES_DX_PIVOT') IS NOT NULL 
DROP TABLE #PROCEDURES_DX_PIVOT

IF OBJECT_ID('tempdb..#PATIENTS') IS NOT NULL 
DROP TABLE #PATIENTS
IF OBJECT_ID('tempdb..#PATIENTS_FINAL') IS NOT NULL 
DROP TABLE #PATIENTS_FINAL

/** DEFINE TABLE AND PARAMETERS  **/

/** CREATE THE TABLE FOR THE PATIENTS  **/
CREATE TABLE #PATIENTS
(
        Patient_Account VARCHAR(20) NOT NULL,
        Visit_Key       VARCHAR(20) NOT NULL
)

/**  DECLARE THE VARIABLES FOR THE TEMP PIVOT TABLES THAT ARE NEEDED FOR THE THREE SECTIONS  **/
DECLARE @SQLPIVOT_CPT AS NVARCHAR(MAX)
DECLARE @SQLPIVOT_DRG AS NVARCHAR(MAX)
DECLARE @SQLPIVOT_DX AS NVARCHAR(MAX)

/**  DECLARE THE VARIABLE NAME THAT WILL HOLD THE COLUMN NAMES LIKE CPT_1, CPT2, CPT_3, etc OR DRG_1, DRG_2,etc, OR DX_1, DX_2, DX_3, DX_4, etc  **/
DECLARE @PivotColumns_CPT AS NVARCHAR(MAX)
DECLARE @PivotColumns_DRG AS NVARCHAR(MAX)
DECLARE @PivotColumns_DX AS NVARCHAR(MAX)


/** CPT CODES  **/

SELECT      pv.pt_id,
            pv.vst_key,
            pv.proc_eff_full_date,
            pv.prio_cd,
            'CPT_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(PARTITION BY pv.pt_id ORDER BY pv.prio_cd)) AS 'CPT_Rank',
            pv.proc_cd,
            pv.alt_clasf_desc,
            hrp.Procedure_Type,
            UPPER(pv.resp_pract_rpt_name) AS 'Surgeon'
INTO        #PROCEDURES_CPT
FROM        smsdss.proc_v AS pv INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON pv.proc_cd = hrp.Clinical_Code AND hrp.Report_Type = 'CPT'
WHERE       pv.proc_eff_full_date BETWEEN '10/01/2020' AND '09/30/2021'

/** DRG CODES  **/

SELECT      vv.pt_id,
            vv.vst_key,
            vv.end_full_date,
            vv.drg_no,
            UPPER(drg.DRGDesc) AS 'DRG_Description',
            hrp.Procedure_Type,
            UPPER(vv.adm_pract_rpt_name) AS 'Admitting Physician'
INTO        #PROCEDURES_DRG
FROM        smsdss.vst_v AS vv INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON vv.drg_no = hrp.Clinical_Code AND hrp.Report_Type = 'DRG' LEFT OUTER JOIN
            smsdss.DRGMstr AS drg ON vv.drg_no = drg.DRGNo  AND drg.DRGVers = 'MS-V38'
WHERE       vv.end_full_date BETWEEN '10/01/2020' AND '09/30/2021'

/** DX CODES  **/

SELECT      dx.pt_id,
            dx.vst_key,
            dx.dx_eff_full_date,
            dx.dx_type_desc,
            'Dx_' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER(PARTITION BY dx.pt_id, dx.dx_type_desc ORDER BY dx.prio_cd)) AS 'Dx_Rank',
            dx.prio_cd,
            dx.dx_cd,
            dx.clasf_desc,
            hrp.Procedure_Type 
INTO        #PROCEDURES_DX
FROM        smsdss.dx_grp_v AS dx INNER JOIN
            dbo.BETHESDA_HIGH_RISK_PROCEDURES AS hrp ON dx.dx_cd = hrp.Clinical_Code AND hrp.Report_Type = 'ICD-10'
WHERE       dx.dx_eff_full_date BETWEEN '10/01/2020' AND '09/30/2021'
GROUP BY    dx.pt_id,
            dx.vst_key,
            dx.dx_eff_full_date,
            dx.dx_type_desc,
            dx.prio_cd,
            dx.dx_cd,
            dx.clasf_desc,
            hrp.Procedure_Type 
ORDER BY    dx.pt_id,
            dx.dx_type_desc,
            dx.prio_cd
 
 /** PIVOT DX  **/
 SELECT     @PivotColumns_DX = COALESCE(@PivotColumns_DX +  ', ' , '') + Dx_Rank
 FROM       #PROCEDURES_DX 
 GROUP BY   Dx_Rank
 ORDER BY   Dx_Rank

SELECT @PivotColumns_DX

 SET @SQLPIVOT_DX = N'SELECT    pt_id, vst_key, ' + @PivotColumns_DX + ' 
                      INTO      #PROCEDURES_DX_PIVOT
                      FROM      #PROCEDURES_DX
                            PIVOT (MAX(dx_cd)
                                FOR Dx_Rank IN (' + @PivotColumns_DX + ')) AS tdx'
SELECT @SQLPIVOT_DX

EXEC sp_executesql @SQLPIVOT_DX

SELECT * FROM #PROCEDURES_DX_PIVOT

直到我在最后一行添加尝试从临时表#PROCEDURES_DX_PIVOT 中选择以查看我拥有的内容之前,我没有收到任何错误。运行对象无效的代码时出现错误。就像代码没有被执行一样。我创建了一个简单的测试来查看它是否是一个权限,即使我没有收到错误并且我能够创建一些东西。然后我从SELECT @SQLPIVOT_DX 获取输出,那时我能够看到我的结果以及我如何看到患者是否有 Dx_1 和 Dx_2,它们位于两条不同的行上,这不是我想要的。我想把所有东西都放在一条线上。

任何帮助将不胜感激。如果我的方法不正确,请告诉我。

【问题讨论】:

  • 这看起来像 SQL Server 代码,所以我已经相应地编辑了标签。如果不是,请更新。
  • 完美,谢谢尼克
  • 我也将你的标题更新为更有意义的东西
  • @RodgerDjr 你可以在你的问题中添加数据脚本来查看输出吗?
  • 您能否拥有固定数量的 Dx_ 列(额外的列将具有 null 值),这样您就可以避免动态 SQL 使事情变得非常复杂

标签: sql sql-server tsql dynamic-pivot


【解决方案1】:

要在同一行中获取 Patient_Account 的每个 Dx_Code,您需要 GROUP BY Patient_Account,并使用一些聚合函数(如 MIN() 或 MAX())来获取 Dx_Code 和 Dx_Desctiption。

进行动态查询的另一种方式:

SELECT STRING_AGG(query_piece, '')

FROM (

(SELECT 'SELECT Patient_Account,' AS query_piece)

UNION ALL

(SELECT 
    CONCAT('MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Code END) AS ', Dx_Rank, ', ', 
    'MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Desctiption END) AS ', Dx_Rank, '_Description, ')
FROM Diagnosis
GROUP BY Dx_Rank)

UNION ALL

(SELECT 'Visit_Key 
    FROM Diagnosis
    GROUP BY Patient_Account, Visit_Key;')) AS dynamic_query;

动态查询的结果是你必须执行的查询才能得到想要的结果:

SELECT Patient_Account,MIN(CASE WHEN Dx_Rank = 'Dx_1' THEN Dx_Code END) AS Dx_1, MIN(CASE WHEN Dx_Rank = 'Dx_1' THEN Dx_Desctiptio END) AS Dx_1_Description, MIN(CASE WHEN Dx_Rank = 'Dx_2' THEN Dx_Code END) AS Dx_2, MIN(CASE WHEN Dx_Rank = 'Dx_2' THEN Dx_Desctiptio END) AS Dx_2_Description, MIN(CASE WHEN Dx_Rank = 'Dx_3' THEN Dx_Code END) AS Dx_3, MIN(CASE WHEN Dx_Rank = 'Dx_3' THEN Dx_Desctiptio END) AS Dx_3_Description, Visit_Key 
    FROM Diagnosis
    GROUP BY Patient_Account, Visit_Key;

输出问题中的示例数据:

Patient_Account Dx_1 Dx_1_Description Dx_2 Dx_2_Description Dx_3 Dx_3_Description Visit_Key
123456789 J20 Left Hip is Broken A32 Left Knee is broken R4786 Left Ankle is broken #PAS203234
987654321 DF346 Right Arm is broken DT342.12 Right Wrist is broken #PAS435678

Sql-Server 2016 中的动态查询(无 string_agg() 函数):

SELECT STUFF((  
        SELECT ' ' + query_piece  
        FROM ((SELECT 'SELECT Patient_Account,' AS query_piece)

        UNION ALL

        (SELECT TOP 100
            CONCAT('MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Code END) AS ', Dx_Rank, ', ', 
            'MIN(CASE WHEN Dx_Rank = ''', Dx_Rank, ''' THEN Dx_Desctiption END) AS ', Dx_Rank, '_Description, ')
        FROM Diagnosis
        GROUP BY Dx_Rank
        ORDER BY Dx_Rank ASC)

        UNION ALL

        (SELECT 'Visit_Key FROM Diagnosis GROUP BY Patient_Account, Visit_Key')) as x
        FOR XML PATH('')
     ), 1, 1, '');

【讨论】:

  • 好的,让我试试这个,我想这正是我想要的。我确实意识到我想选择我的表我必须在我的变量中添加一行来获取数据,我将努力将你的代码添加到我的。谢谢!!
  • 我没有看到我是如何循环获得 Dx_Rank 中不同的 Dx_1、Dx_2、Dx3。
  • 它不是“循环”,检查我的动态查询中的第二个UNION ALL,它是按Dx_Rank分组的,所以结果是按Dx_Rank的一行(你可以用WHERE子句限制它) ,结果是 Dx_Rank 的两个 CASE 语句(一个用于 Dx_Code,另一个用于 Dx_Desctiption)。这是一种不同的方法。
  • 我正在尝试并感谢我在 SQL 2016 上的帮助,因此我没有函数 STRING_AGG。还有什么我可以用的吗?
  • STRING_AGG() 连接每个动态查询片段(行)以将“所需”查询放入一个字段中。您可以使用 FOR XML PATHSTUFF 函数代替 string_agg() 函数,我更新了我的答案。
【解决方案2】:

您好,很遗憾您没有提供所有相关实体来重现您的场景并执行您的完整查询,但最后一个主要问题似乎很清楚

直到我在最后一行添加尝试从临时表#PROCEDURES_DX_PIVOT 中选择以查看我拥有的内容之前,我没有收到任何错误。运行对象无效的代码时出现错误。就像代码没有被执行一样。

问题是您在 sp_executesql 的执行范围内创建了临时表,但您尝试在此范围之外使用它,但它不存在

这是同一问题的简单完整演示

准备

USE tempdb
GO
DROP TABLE IF EXISTS T
GO 
create table T(id int)
GO

我们在同一个范围内创建临时表的工作示例

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
SELECT * INTO #PROCEDURES_DX_PIVOT FROM T
GO
SELECT * FROM #PROCEDURES_DX_PIVOT
GO -- OK, since the temp table was created in the same scope as we call it

错误!无效的对象名称“#PROCEDURES_DX_PIVOT”

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
DECLARE @SQLPIVOT_DX NVARCHAR(MAX)
SET @SQLPIVOT_DX = N'SELECT * INTO #PROCEDURES_DX_PIVOT FROM T'
SELECT @SQLPIVOT_DX
EXEC sp_executesql @SQLPIVOT_DX

SELECT * FROM #PROCEDURES_DX_PIVOT
GO -- Invalid object name '#PROCEDURES_DX_PIVOT'

可选的解决方案是从动态查询中调用表

DROP TABLE IF EXISTS #PROCEDURES_DX_PIVOT
GO 
DECLARE @SQLPIVOT_DX NVARCHAR(MAX)
SET @SQLPIVOT_DX = N'SELECT * INTO #PROCEDURES_DX_PIVOT FROM T; SELECT * FROM #PROCEDURES_DX_PIVOT;'
SELECT @SQLPIVOT_DX
EXEC sp_executesql @SQLPIVOT_DX
GO -- Invalid object name '#PROCEDURES_DX_PIVOT'

回到你的代码

这与您的代码中的问题相同:

SET @SQLPIVOT_DX = N'SELECT    pt_id, vst_key, ' + @PivotColumns_DX + ' 
                      INTO      #PROCEDURES_DX_PIVOT
                      FROM      #PROCEDURES_DX
                            PIVOT (MAX(dx_cd)
                                FOR Dx_Rank IN (' + @PivotColumns_DX + ')) AS tdx'
SELECT @SQLPIVOT_DX

EXEC sp_executesql @SQLPIVOT_DX -- here you use select into to create the temp table

SELECT * FROM #PROCEDURES_DX_PIVOT --  and here you use it outside of the scope which it was created

【讨论】:

  • 我确实提供了一小部分数据样本,但我无法提供表格,因为该数据是 PHI。我不确定我是否理解你的例子。您正在将数据插入到您选择的同一个表中?有没有办法使用我插入到临时表中的数据,或者我是否需要在我的数据库中创建一个表并将其用作我的临时表,然后我可以随时调用它而不必担心范围。重新思考 - 那么表格将不是动态的,我又回到了我开始的地方。
  • 我能够手动运行我的查询。当我这样做时,我会为每个诊断获得一行。意思是患者 A 我在 Dx_1 中得到诊断 1 的一行,Dx_2 为 NULL,然后我在 Dx_2 中得到诊断 2 且 Dx_1 为 NULL 的患者 A 的另一行。
  • 您好@RodgerDjr,我仍然没有看到示例表和数据。例如,在您的代码中使用了表 smsdss.dx_grp_vsmsdss.vst_v。请注意,"table that looks like" 不是提供信息的方式。您应该提供查询以创建相关表并插入示例数据。如果您不能提供真正的完整结构,那么您应该围绕一个可以呈现问题的假简单表格来构建您的问题 - 用于讨论的示例表格。此时我注意到了您的最后一个问题/问题,我可以像我一样快速解决。
  • 如果您可以向任何相关表提供以 DDL+DML 开头的完整示例,并且您的查询使用此示例表,那么我很乐意再看一下问题的其余部分。如上所述,此时我专注于您在线程中的最后一个问题:-)
  • Ronen,是的,谢谢你我发现我需要在查询字符串中添加 SELECT 语句,然后当它执行时我会得到我的结果。所以非常感谢你,这对理解这部分有很大帮助。有了这些知识和@nachospiu 发布的内容,我想我已经很接近了。
猜你喜欢
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多