基于设置的解决方案以逗号分隔的有序行从 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 1:(每个id一行)eid, name, rank
- Table 2: (每个id一行) eid, state, calendar
- Table 3:(多行具有相同的id)eid,联系人姓名,公司
- Table 4:(每个id一行)eid, title, code
我需要一个摘录,以逗号分隔的格式从每个 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
我正在使用 SQL Server 2012,我需要创建一个提取文件,其中包含一个逗号分隔的字符串,其中包含来自 4 个不同 table 中每一个的数据,并按其 ID 对其进行分组。 (这是问题的简单表示 - 我正在处理 table 3 中数以万计的唯一 eid 和数百万行,其中每个 eid 可能有数千条匹配记录。)
- Table 1:(每个id一行)eid, name, rank
- Table 2: (每个id一行) eid, state, calendar
- Table 3:(多行具有相同的id)eid,联系人姓名,公司
- Table 4:(每个id一行)eid, title, code
我需要一个摘录,以逗号分隔的格式从每个 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