如何在 SQL Server 中以字符串形式获取详细表记录

Posted

技术标签:

【中文标题】如何在 SQL Server 中以字符串形式获取详细表记录【英文标题】:How to get detail table records as string in SQL Server 【发布时间】:2016-12-26 06:18:47 【问题描述】:

我在数据库中有一个表,其中包含以下医院名称:

+------------+--------------+
| HospitalID | HospitalName |
+------------+--------------+
|          1 | Hosp1        |
|          2 | Hosp2        |
|          3 | Hosp3        |
|          4 | Hosp4        |
+------------+--------------+

还存在另一个表,其中包含以下活动名称:

+------------+--------------+
| ActivityID | ActivityName |
+------------+--------------+
|          1 | Act1         |
|          2 | Act2         |
|          3 | Act3         |
|          4 | Act4         |
|          5 | Act5         |
+------------+--------------+

这些表之间存在 N*M 关系,即每家医院可以开展不同的活动。因此需要另一个表,如下所示:

+----+------------+------------+
| ID | HospitalID | ActivityID |
+----+------------+------------+
|  1 |          1 |          1 |
|  2 |          1 |          2 |
|  3 |          1 |          5 |
|  4 |          2 |          1 |
|  5 |          2 |          3 |
|  6 |          3 |          2 |
+----+------------+------------+

我想编写一个 select 语句,在字符串字段中选择医院名称及其相关活动,如下所示:

+--------------+------------------+
| HospitalName |  ActivityNames   |
+--------------+------------------+
| Hosp1        | Act1, Act2, Act5 |
| Hosp2        | Act1, Act3       |
| Hosp3        | Act2             |
| Hosp4        |                  |
+--------------+------------------+

我已经使用游标为 ActivityNames 字段的函数编写了 select 语句,但它没有优化,并且系统性能随着记录数量的增加而降低。

关于如何解决这个问题的任何解决方案或建议?

【问题讨论】:

第一个建议 - 升级你的数据库。 Microsoft 不再支持 Sql server 2005。 【参考方案1】:

您只需通过选择即可完成此操作。不需要循环或光标。循环会导致性能下降。

所以架构将是

CREATE TABLE #HOSPITAL( HOSPITALID INT, HOSPITALNAME VARCHAR(20))

INSERT INTO #HOSPITAL
SELECT 1, 'HOSP1' 
UNION ALL 
SELECT 2 , 'HOSP2' 
UNION ALL 
SELECT 3 ,'HOSP3' 
UNION ALL 
SELECT 4 , 'HOSP4' 



CREATE TABLE #ACTIVITY( ActivityID INT, ActivityName VARCHAR(50) )

INSERT INTO #ACTIVITY                          
SELECT          1,  'Act1'    
UNION ALL     
SELECT          2,  'Act2'  
UNION ALL        
SELECT          3,  'Act3' 
UNION ALL         
SELECT          4,  'Act4'
UNION ALL          
SELECT          5,  'Act5'         


CREATE TABLE #HOSPITAL_ACT_MAP(ID INT, HospitalID INT, ActivityID INT)

INSERT INTO #HOSPITAL_ACT_MAP
SELECT 1, 1, 1 
UNION ALL 
SELECT 2, 1, 2 
UNION ALL 
SELECT 3, 1, 5 
UNION ALL 
SELECT 4, 2, 1 
UNION ALL 
SELECT 5, 2, 3 
UNION ALL 
SELECT 6, 3, 2 

然后使用 CTE 进行如下选择

;WITH CTE AS (
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID
)

SELECT  HOSPITALNAME
, (SELECT STUFF((SELECT ','+ActivityName FROM CTE C1 
WHERE  C1.HOSPITALNAME = C.HOSPITALNAME 
FOR XML PATH('')),1,1,'')) 
FROM CTE C
GROUP BY HOSPITALNAME

从评论编辑

如果您不能使用CTEStuff,请选择方法2

DECLARE @TAB TABLE (HOSPITALNAME VARCHAR(20),ActivityName VARCHAR(20) )

INSERT INTO @TAB
SELECT DISTINCT H.HOSPITALNAME, A.ActivityName FROM #HOSPITAL_ACT_MAP HA
INNER JOIN #HOSPITAL H ON HA.HospitalID = H.HOSPITALID
INNER JOIN #ACTIVITY A ON HA.ActivityID = A.ActivityID

SELECT HOSPITALNAME, SUBSTRING(ACTIVITIES,1, LEN(ACTIVITIES)-1) FROM(

SELECT DISTINCT HOSPITALNAME,(SELECT ActivityName+',' FROM @TAB T1 
WHERE  T1.HOSPITALNAME = T.HOSPITALNAME 
FOR XML PATH('') ) AS ACTIVITIES FROM @TAB T

)A

注意:出于性能目的,我将中间结果存储在@TAB(表变量)上。如果你愿意,你可以直接用 Sub Query 查询它。

【讨论】:

不确定 sql server 2005 是否支持stuff,很确定它不支持 cte。但我似乎记得它确实支持for xml... 使用 CTE 和 STUFF 的书面方法在 SQL Server 2005 和 2008 中对我来说都很好。感谢您宝贵的 cmets。 @shahram Akbarinasaji,您使用了内部联接。那么您如何获得“HOSP4”值。它与活动和映射无关。 @ShakeerMirza,您使用了内部连接。那么您如何获得“HOSP4”值。它与活动和映射无关。 @Mansoor ,将 改为 即可解决此问题。【参考方案2】:
using STUFF function to achieve your result :

CREATE TABLE #Hospital(HospitalID INT,HospitalName VARCHAR(100))
CREATE TABLE #Activity(ActivityID INT,ActivityName VARCHAR(100))
CREATE TABLE #RelationShip(Id INT,HospId INT,ActId INT)
CREATE TABLE #ConCat(HospitalID  INT ,HospName VARCHAR(100), ActName   
VARCHAR(100),UpFlag TINYINT DEFAULT(0))
DECLARE @HospId INT = 0,@String VARCHAR(200) = ''

INSERT INTO #Hospital(HospitalID ,HospitalName )
SELECT 1,'Hosp1' UNION ALL
SELECT 2,'Hosp2' UNION ALL
SELECT 3,'Hosp3' UNION ALL
SELECT 4,'Hosp4'    

INSERT INTO #Activity(ActivityID ,ActivityName )
SELECT  1,'Act1' UNION ALL 
SELECT  2,'Act2' UNION ALL 
SELECT  3,'Act3' UNION ALL 
SELECT  4,'Act4' UNION ALL 
SELECT  5,'Act5' 

INSERT INTO #RelationShip(ID,HospId,ActId)
SELECT  1 ,          1 ,          1 UNION ALL
SELECT    2 ,          1 ,          2 UNION ALL
SELECT    3 ,          1 ,          5 UNION ALL
SELECT    4 ,          2 ,          1 UNION ALL
SELECT    5 ,          2 ,          3 UNION ALL
SELECT    6 ,          3 ,          2 

SELECT HospitalName , STUFF(  ( SELECT  ',' + ActivityName FROM #Activity   
JOIN #RelationShip ON ActId = ActivityID WHERE HospId = HospitalID FOR XML   
PATH('') ),1,1,'')  
FROM #Hospital
GROUP BY HospitalID,HospitalName 

***FOR SQLServer2005 Use below code***

  INSERT INTO #ConCat (HospitalID ,HospName) 
  SELECT DISTINCT HospitalID ,HospitalName 
  FROM #Hospital

  WHILE EXISTS(SELECT 1 FROM #ConCat WHERE UpFlag = 0)
  BEGIN

     SELECT @HospId =  HospitalID FROM #ConCat WHERE UpFlag = 0 ORDER BY   
     HospitalID

     SET @String = ''
     SELECT @String = ISNULL(@String,'') + CAST(A.ActivityName AS VARCHAR) +    
     ',' FROM 
      (
        SELECT ActivityName 
        FROM #RelationShip 
        JOIN #Activity ON ActId = ActivityID
        WHERE HospId = @HospId
      ) A 

      UPDATE #ConCat SET UpFlag = 1,ActName = CASE WHEN @String = '' THEN    
       @String ELSE SUBSTRING(@String,0,LEN(@String) ) END WHERE HospitalID    
       = @HospId

     END

     SELECT * FROM #ConCat

【讨论】:

不确定 sql server 2005 是否支持stuff,但我似乎记得它确实支持for xml... @ZoharPeled 2005 支持的东西 @t-clausen.dk 很高兴知道,如果我能发明那台时光机 :-) 但是在这个微软文档中声明它从 2008 年开始支持 msdn.microsoft.com/en-us/library/ms188043.aspx@t-clausen.dk @ShakeerMirza 认为你是对的。我正在使用这个link【参考方案3】:

您可以使用 json 来提高前端的性能。 如果您使用的是开源数据库,则没有特定的解决方案。

尝试使用 IBM db2 或 ORACLE 数据库来确保您的应用程序的性能。

然后生成json数据。你会发现速度的提升

【讨论】:

Faruk 他已经标记了Sqlserver,所以在此基础上给出答案。 我使用 SQL Server 2005 作为数据库服务器。我的问题是如何获取 ActivityNames 字段。您介意提供更详细的信息吗?

以上是关于如何在 SQL Server 中以字符串形式获取详细表记录的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL Server Management Studio 中以“真实”CSV 格式获取导出输出?

如何从索引中以字符串形式获取突出显示数据或如何将其打印出来?

在 SQL Server 中以日期时间格式转换日期时间字符串

在 Linux 中以 UTF-8 字符串形式获取环境变量

在 C# 中以字符串形式获取类的名称

有没有办法在黑莓中以字符串的形式获取异常的堆栈跟踪?