基于设置的解决方案以逗号分隔的有序行从 SQL 服务器中提取数据?

Set based solution to extract data from SQL server in comma delimited, ordered lines?

我正在使用 SQL Server 2012,我需要创建一个提取文件,其中包含一个逗号分隔的字符串,其中包含来自 4 个不同 table 中每一个的数据,并按其 ID 对其进行分组。 (这是问题的简单表示 - 我正在处理 table 3 中数以万计的唯一 eid 和数百万行,其中每个 eid 可能有数千条匹配记录。)

我需要一个摘录,以逗号分隔的格式从每个 table 中获取信息,每个人的记录在一组中并按特定顺序 - 首先是 table 1 的信息,然后 table 2 等等

For instance:
e01,jon jones,1   --from table 1
e01,ca,spring         --from table 2
e01,fred mac, abc     --from table 3
eo1,freddie may, xyz  --from table 3
e01,president,0001    --from table 4

我的第一个想法是使用变量和 CONCAT 将信息放入每个 table 的 XML 字符串中,然后使用游标将每个 eid 的字符串 select 放入 --然而,这会为每个 eid 创建一个单独的 xml 文件,我需要将所有记录集中在一个文件中。当我尝试将输出从 SSMS 发送到文件时,结果被截断了 - 我猜是由于 XML 字符限制,但我不确定。

如果有任何关于如何更好地处理此问题的想法,我将不胜感激。是否有我忽略的基于集合的解决方案?

谢谢


这是我已有的代码:

DECLARE @rec1 VARCHAR(1000)
DECLARE @rec2 VARCHAR(1000)
DECLARE @rec3 VARCHAR(MAX)
DECLARE @rec4 VARCHAR(1000)
DECLARE @FTrec VARCHAR(4)
DECLARE @eid INT
DECLARE @newLine AS CHAR(1) = CHAR(10)
DECLARE @count INT
DECLARE @result VARCHAR(MAX)
DECLARE @rID INT


set @count = (select count(*) from tmp_openSDrec)
set @rID = 1

WHILE @count > 0
BEGIN

set @eid = (select eid from tmp_openSDrec where rID = @rID)

set @rec1=(
    SELECT CONCAT(recordID,',',CAST(LayoutVersion as VARCHAR(3)),',',submissionType,',',terYear,',',zzType,',',CAST(terVCCode as VARCHAR(6)),',',
                    CDSCode,',',physicalterName,',',terAddr,',',terCity,',',terState,',',terZip,',',adminContFirstName,',',adminContLastName,',',
                    adminContPhone,',',adminContEmail,',',techContFirstName,',',techContLastName,',',techContPhone,',',techContEmail,',',CAST(numTranscripts as VARCHAR(4))
                    ,',',CAST(carnegieUnitConversionFactor as VARCHAR(6)),',',calendarCY,',',calendarCY1,',',calendarCY2,',',calendarCY3,',',CAST(drgMatchingfieldName as VARCHAR(2)),',',
                    extractDate,',',eor)
    from tmp_openSDrec
    where eid = @eid
    FOR XML Path(''))

SET @rec2=(
    SELECT CONCAT(recordID,',',CAST(terVCCode as VARCHAR(6)),',',CAST(eid as VARCHAR(20)),',',empNumber,',',firstName,',',lastName
                    ,',',empCity,',',empState,',',empZip,',',CONVERT(VARCHAR(8),dob,112),',',gender,',',rank,',',hireDate,',',divRank,',',
                    region,',',supConsent,',',eor)
    from tmp_open01rec
    where eid = @eid
    FOR XML Path(''))

SELECT @rec3=(
    select CONCAT(recordID,',',rank,',',terAttended,',',terVCCode,',',terYear,',',term,',',blockSchedule,',',workInProgress,',',
                    CAST(fieldID as VARCHAR(25)),',',fieldName,',',colPrepInd,',',creditsAtmpt,',',creditsEarned,',',fieldrank,',',subjectArea,',',eor,@newLine)
    from tmp_open02rec
    where eid = @eid
    FOR XML PATH(''))

SET @rec4=(
    select CONCAT(recordID,',',language1,',',language2,',',' ',',',' ',',',' ',',',' ',',',eor)
    from tmp_open05rec
    where eid = @eid
    order by recordID
    FOR XML Path(''))

SET @FTrec='FT,*'

    set @result = (select @rec1 + CHAR(10) +  @rec2 + CHAR(10) + @rec3 + @rec4 + CHAR(10) + @FTrec + CHAR(10))
    select @result for XML Path(''), type



    set @count = @count - 1
    set @rID = @rID + 1

END

我现在在想,如果我能绕过 XML 字符限制,脚本就可以运行。我一直在尝试向 WHILE 循环添加 bcp 命令:

DECLARE @SQLCmd as VARCHAR(500)
DECLARE @fileName VARCHAR(50)
set @fileName = 'c:\bcpoutput.txt'
SELECT  @SQLCmd = 'bcp ' + '"SELECT   ' + @result + ' FOR XML PATH(''''), TYPE "' + ' queryout '  + @FileName + ' -w -T -S' + @@SERVERNAME
EXECUTE master.dbo.xp_cmdshell @SQLCmd

但我似乎也无法让它工作... 结果是:

output
usage: bcp {dbtable | query} {in | out | queryout | format} datafile
  [-m maxerrors]            [-f formatfile]          [-e errfile]
  [-F firstrow]             [-L lastrow]             [-b batchsize]
  [-n native type]          [-c character type]      [-w wide character type]
  [-N keep non-text native] [-V file format version] [-q quoted identifier]
  [-C code page specifier]  [-t field terminator]    [-r row terminator]
  [-i inputfile]            [-o outfile]             [-a packetsize]
  [-S server name]          [-U username]            [-P password]
  [-T trusted connection]   [-v version]             [-R regional enable]
  [-k keep null values]     [-E keep identity values]
  [-h "load hints"]         [-x generate xml format file]
  [-d database name]        [-K application intent]
NULL

是这样的吗?

SELECT eid + ',' + x + ',' + y + ',' as results FROM (
    SELECT tblnum, eid, x, y FROM (
        SELECT 1 AS tblnum, eid, name AS x, rank AS y FROM table1
        UNION ALL
        SELECT 2 AS tblnum, eid, state AS x, calendar AS y FROM table2
        UNION ALL
        SELECT 3 AS tblnum, eid, contactname AS x, company AS y FROM table3
        UNION ALL
        SELECT 4 AS tblnum, eid, title AS x, code AS y FROM table4
    ) combined
    ORDER BY eid, tblnum
)

不过我想我找到了更好的解决方案。我放弃了这个并使用有序的 Union All 而不是分配所有这些变量。然后我就可以把它输入到一个温度 table,然后 BCP 将那个温度 table 输出到一个 csv 文件——然后冲洗、清洗、重复每个开斋节。这是我的最终解决方案:

DECLARE @eID INT
DECLARE @count INT
DECLARE @rID INT
DECLARE @SQLCmd as VARCHAR(500)
DECLARE @fileName VARCHAR(50)

set @count = (select count(*) from tmp_openSDrec)
set @rID = 1 --because eID is sequential and unique in tmp_openSDrec

WHILE @count > 0
BEGIN

set @eID = (select eID from tmp_openSDrec where sdID = @rID)

--drop the tmp_opentemp table
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmp_opentemp]') AND TYPE IN (N'U'))
DROP TABLE [dbo].[tmp_opentemp]

SELECT field1 into tmp_opentemp
FROM
(

        SELECT CONCAT(recordID,',',CAST(LayoutVersion as VARCHAR(3)),',',submissionType,',',terYear,',',zzType,',',CAST(terVCCode as VARCHAR(6)),',',
                    CDSCode,',',physicalterName,',',terAddr,',',terCity,',',terState,',',terZip,',',adminContFirstName,',',adminContLastName,',',
                    adminContPhone,',',adminContEmail,',',techContFirstName,',',techContLastName,',',techContPhone,',',techContEmail,',',CAST(numTranscripts as VARCHAR(4))
                    ,',',CAST(carnegieUnitConversionFactor as VARCHAR(6)),',',calendarCY,',',calendarCY1,',',calendarCY2,',',calendarCY3,',',CAST(drgMatchingfieldName as VARCHAR(2)),',',
                    extractDate,',',eor)
        as field1, 1 as sortOrder, recordID
        from tmp_openSDrec
        where eID = @eID
  UNION ALL

        SELECT CONCAT(recordID,',',CAST(terVCCode as VARCHAR(6)),',',CAST(eid as VARCHAR(20)),',',empNumber,',',firstName,',',lastName
                    ,',',empCity,',',empState,',',empZip,',',CONVERT(VARCHAR(8),dob,112),',',gender,',',rank,',',hireDate,',',divRank,',',
                    region,',',supConsent,',',eor)
        as field1, 2 as sortOrder, recordID
        from tmp_open01rec
        where eID = @eID
  UNION ALL
    -
        SELECT CONCAT(recordID,',',rank,',',terAttended,',',terVCCode,',',terYear,',',term,',',blockSchedule,',',workInProgress,',',
                    CAST(fieldID as VARCHAR(25)),',',fieldName,',',colPrepInd,',',creditsAtmpt,',',creditsEarned,',',fieldrank,',',subjectArea,',',eor)
        as field1, 3 as sortOrder, recordID
        from tmp_open02rec
        where eID = @eID
  UNION ALL

        select CONCAT(recordID,',',language1,',',language2,',',' ',',',' ',',',' ',',',' ',',',eor)
        as field1, 4 as sortOrder, recordID
        from tmp_open05rec
        where eID = @eID

) sq
order by sortOrder, recordID;

SELECT @SQLCmd = 'bcp "select * from [sandbox].dbo.tmp_opentemp" queryout "c:\bcptest.csv" -w -t -T -S && type c:\bcptest.csv >> c:\bcpappendd.csv' ;
EXEC master..xp_cmdshell @SQLCmd;

    set @rID = @rID + 1;

    set @count = @count - 1;



END