对非数值数据使用 Pivot

Using Pivot with non Numerical Data

这是我第一次尝试使用 PIVOT。 我正在使用 Microsoft SQL 服务器。

所以这是我的问题,我一直在阅读 Pivot 并决定它对于将患者数据导出到格式化文件(即报告)的项目非常有用,可以打印出来等等。 .

VPatientPlusAllergyData 是一个视图,将此显示为样本结果,为了便于阅读,一些数据被删减

strPatientFullName  strAllergy  strAllergyMedication
------------------------------------------------------------
Smith, John Henry   Dogs        Pounces         
Smith, John Henry   Dogs        Orange Juice        
Smith, John Henry   Mustard     Ketchup         
Smith, John Henry   Mustard     Sugar           

这就是我想要的结果

 strPatientFullName strAllergy1 strAllergy1Medications   strAllergy2    strAllergy2Medications
------------------------------------------------------------------------------------------------------
Smith, John Henry   Dogs        Pounces, OrangeJuice     Mustard        Ketchup, Sugar

在 W3School 上阅读、观看 Youtube 视频甚至阅读本网站上的一些文章后,我想知道我正在尝试做的事情是否可行

下面是一个代码片段,但我卡在了我应该在 IN 语句中放入什么,当我开始质疑 PIVOT 是否能解决我的特定问题时。

GO
SELECT 
     strPatientFullName
    ,strStreetAddress
    ,strCity
    ,strState
    ,strZipcode
    ,strPrimaryPhoneNumber
    ,strSecondaryPhoneNumber
    ,blnSmoker
    ,decPackYears
    ,blnHeadOfHousehold
    ,dtmDateOfBirth
    ,strSex
    ,strAllergy
    ,strAllergyMedication
    ,strEmailAddress
    ,strRecordCreator

FROM ( SELECT * FROM VPatientPlusAllergyData ) PatientAllergyData

PIVOT
    (
        MAX(strAllergyMedication)
        FOR strAllergy
        IN ()
    )

GO

希望更熟悉 Pivot 的人能告诉我我遗漏了什么,或者启发我找到更有效的解决方案。

感谢您的帮助

****** 编辑:我已经决定虽然我很乐意将这种操作放在服务器端,但对于我的特定应用程序,创建大量视图然后执行 SELECT 在客户端查询并以这种方式连接它们,然后实现 "EXPORT PROCESSING" 屏幕。 我感谢所有的帮助,也许有一天我会写一个脚本并让它在服务器端执行,但目前这项工作已经足够好了 ******

这是一个示例,说明如何使用 STUFF 语句、条件聚合和动态 SQL。

DECLARE @SQL NVARCHAR(MAX) = '';
SELECT @SQL += '
     , MAX(CASE WHEN RN = ' + RN + ' THEN strAllergy END) strAllergy' + RN + '
     , MAX(CASE WHEN RN = ' + RN + ' THEN strAllergyMedications END) strAllergyMedications' + RN
FROM (
    SELECT CAST(ROW_NUMBER() OVER (PARTITION BY strPatientFullName, strAllergy ORDER BY (SELECT NULL)) AS VARCHAR(5)) RN
    FROM VPatientPlusAllergyData) T
GROUP BY RN;

SELECT @SQL = 'SELECT strPatientFullName' + @SQL + '
FROM (
    SELECT strPatientFullname
         , strAllergy
         , STUFF((SELECT '', '' + strAllergyMedication FROM VPatientPlusAllergyData WHERE strPatientFullName = T.strPatientFullName AND strAllergy = T.strAllergy FOR XML PATH ('''')), 1, 2, '''') strAllergyMedications
         , ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
    FROM VPatientPlusAllergyData T
    GROUP BY strPatientFullname, strAllergy) T
GROUP BY strPatientFullname;';

PRINT @SQL;
EXEC(@SQL);

正如 scsimon 在评论中提到的,如果可能存在任意数量的过敏,则可能需要动态 SQL。 stuff 语句是将逗号分隔值放入单个列中的一种方法。条件聚合的工作方式与 PIVOT 通常工作的方式相同,但比普通 PIVOT 语句更容易 (IMO) 编写和理解。

因此,要获得您想要的结果,您实际上需要以下技术:

  • 对于 strAllergyMedications,您需要 将行连接到定界字符串
  • 然后要将您的行变成列,您需要 PIVOT,但是因为您要旋转 2 列,所以您必须 PIVOT 两次或使用 条件聚合

实现它的主要技巧是通过进行串联并提出过敏的行号来准备您的 table。下面是一个使用 Common Table Expression [CTE] 和 STUFF() 以及子 select XML 来创建分隔字符串并创建行号的示例。

DECLARE @VPatientPlusAllergyData AS TABLE (strPatientFullName  VARCHAR(100), strAllergy  VARCHAR(50), strAllergyMedication VARCHAR(100))
INSERT INTO @VPatientPlusAllergyData VALUES
('Smith, John Henry','Dogs','Pounces')
,('Smith, John Henry','Dogs','Orange Juice')
,('Smith, John Henry','Mustard','Ketchup')
,('Smith, John Henry','Mustard','Sugar')

;WITH cte AS (
    SELECT DISTINCT
       v1.strPatientFullName
       ,v1.strAllergy
       ,strAllergyMedications = STUFF(
          (SELECT ', ' + v2.strAllergyMedication
            FROM
                @VPatientPlusAllergyData v2
            WHERE
             v1.strPatientFullName = v2.strPatientFullName
             AND v1.strAllergy = v2.strAllergy
            FOR XML PATH(''))
            ,1,2,'')
       ,AllergyRowNum = DENSE_RANK() OVER (PARTITION BY v1.strPatientFullName ORDER BY v1.strAllergy)
    FROM
       @VPatientPlusAllergyData v1
)

SELECT
    strPatientFullName
    ,strAllergy1 = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergy END)
    ,strAllergy1Medications = MAX(CASE WHEN AllergyRowNum = 1 THEN strAllergyMedications END)
    ,strAllergy2 = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergy END)
    ,strAllergy2Medications = MAX(CASE WHEN AllergyRowNum = 2 THEN strAllergyMedications END)
FROM
    cte
GROUP BY
    strPatientFullName

在我准备和发布这篇文章时,@ZLK 写了一个很好的动态方法。