arm*_*men 24 sql sql-server cursors dynamic-sql sql-server-2012
Ι有一些表(例如[Table1],[Table2],[Table3]等)与[ID]作为主键和一个RecTime作为DATETIME每个.
ΑlsoΙ有一个表[Files],用于保存varbinary(max)列中的文件,并引用具有其名称和ID的其他表.
[Table2],[Table3]和其他人有不同的结构,但完全分享[ID]和[RecTime]列[Table1]
以下是可视化数据的快速示例.
DECLARE @Table1 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE @Table2 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE @Table3 as table (
[ID] [bigint]
, [RecTime] [datetime]
)
DECLARE @Files as table (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
/* and some other columns */
)
INSERT INTO @Table1 (
[ID]
, [RecTime]
)
SELECT '1', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '2', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '3', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '4', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '5', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO @Table2 (
[ID]
, [RecTime]
)
SELECT '11', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '12', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '13', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '14', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '15', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO @Table3 (
[ID]
, [RecTime]
)
SELECT '21', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '22', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '23', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '24', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '25', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO @Files (
[ID]
, [tblName]
, [tblID]
, [BinaryData]
)
SELECT '1', 'Table1', '1', 0x010203040506
UNION ALL SELECT '2', 'Table1', '2', 0x010203040506
UNION ALL SELECT '3', 'Table1', '2', 0x010203040506
UNION ALL SELECT '4', 'Table1', '3', 0x010203040506
UNION ALL SELECT '5', 'Table1', '4', 0x010203040506
UNION ALL SELECT '6', 'Table1', '5', 0x010203040506
UNION ALL SELECT '7', 'Table1', '5', 0x010203040506
UNION ALL SELECT '8', 'Table2', '11', 0x010203040506
UNION ALL SELECT '9', 'Table2', '11', 0x010203040506
UNION ALL SELECT '10', 'Table2', '12', 0x010203040506
UNION ALL SELECT '11', 'Table2', '13', 0x010203040506
UNION ALL SELECT '12', 'Table2', '14', 0x010203040506
UNION ALL SELECT '13', 'Table2', '12', 0x010203040506
UNION ALL SELECT '14', 'Table2', '15', 0x010203040506
UNION ALL SELECT '15', 'Table3', '21', 0x010203040506
UNION ALL SELECT '16', 'Table3', '22', 0x010203040506
UNION ALL SELECT '17', 'Table3', '24', 0x010203040506
UNION ALL SELECT '18', 'Table3', '23', 0x010203040506
UNION ALL SELECT '19', 'Table3', '25', 0x010203040506
UNION ALL SELECT '20', 'Table3', '25', 0x010203040506
UNION ALL SELECT '21', 'Table3', '21', 0x010203040506
SELECT * FROM @Table1
SELECT * FROM @Table2
SELECT * FROM @Table3
SELECT * FROM @Files
Run Code Online (Sandbox Code Playgroud)
我如何加入[Files]表到其他表,Name以及ID其中在"[文件]"表中的值导出?
我需要[BinaryData]从[Files]表和[RecTime]表中的相应表引用[Files].
真正的问题是[Table1],[Table2]并且[Table3] 不是唯一被引用的[Files]表.可以创建新表,二进制数据必须存储在[Files]表中.
所以我正在寻找一种动态"加入"它们的方法.
PS我不是这个系统的创造者,不能对它进行任何结构改变,只是试图解决这个问题.
任何帮助,将不胜感激.
一种方法是创建一个包含所有表数据的cte(当然,使用动态sql创建它),然后从文件中选择左连接cte.
这样,动态sql编写和维护非常简单,而且它生成的sql语句非常简单:
DECLARE @SQL varchar(max) = ''
SELECT @SQL = @SQL +' UNION ALL SELECT ID,
RecTime,
'''+ tblName +''' AS TableName
FROM ' + tblName
FROM (
SELECT DISTINCT tblName FROM files
) x
-- replace the first 'UNION ALL' with ';WITH allTables as ('
SELECT @SQL = STUFF(@SQL, 1, 11, ';WITH allTables as (')
+')
SELECT *
FROM Files
LEFT JOIN allTables ON(tblName = TableName AND tblId = allTables.Id)'
Run Code Online (Sandbox Code Playgroud)
你从中获得的sql statemet是:
;WITH allTables as (
SELECT ID, RecTime, 'Table1' AS TableName
FROM Table1
UNION ALL
SELECT ID, RecTime, 'Table2' AS TableName
FROM Table2
UNION ALL
SELECT ID, RecTime, 'Table3' AS TableName
FROM Table3
)
SELECT *
FROM Files
LEFT JOIN allTables ON(tblName = TableName AND tblId = allTables.Id)
Run Code Online (Sandbox Code Playgroud)
执行它:
EXEC(@SQL)
Run Code Online (Sandbox Code Playgroud)
结果:
ID tblName tblID BinaryData ID RecTime TableName
1 Table1 1 123456 1 31.03.2060 00:00:00 Table1
2 Table1 2 123456 2 03.12.1997 00:00:00 Table1
3 Table1 2 123456 2 03.12.1997 00:00:00 Table1
4 Table1 3 123456 3 02.07.2039 00:00:00 Table1
5 Table1 4 123456 4 17.06.1973 00:00:00 Table1
6 Table1 5 123456 5 06.12.2076 00:00:00 Table1
7 Table1 5 123456 5 06.12.2076 00:00:00 Table1
8 Table2 1 123456 NULL NULL NULL
9 Table2 3 123456 NULL NULL NULL
10 Table2 3 123456 NULL NULL NULL
11 Table2 4 123456 NULL NULL NULL
12 Table2 5 123456 NULL NULL NULL
13 Table2 5 123456 NULL NULL NULL
14 Table2 5 123456 NULL NULL NULL
15 Table3 1 123456 NULL NULL NULL
16 Table3 1 123456 NULL NULL NULL
17 Table3 1 123456 NULL NULL NULL
18 Table3 3 123456 NULL NULL NULL
19 Table3 3 123456 NULL NULL NULL
20 Table3 3 123456 NULL NULL NULL
21 Table3 4 123456 NULL NULL NULL
Run Code Online (Sandbox Code Playgroud)
一种解决方案是使用游标为表中的每一行执行一些动态SQL@Files:
-- Copy table variables into temporary tables so they can be referenced from dynamic SQL
SELECT * INTO #Table1 FROM @Table1;
SELECT * INTO #Table2 FROM @Table2;
SELECT * INTO #Table3 FROM @Table3;
-- Create a temporary table for storing the results
CREATE TABLE #results (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
, [RecTime] [datetime]
);
-- Declare placeholders and cursor
DECLARE @ID bigint;
DECLARE @tblName nvarchar(255);
DECLARE @tblID bigint;
DECLARE @BinaryData varbinary(max);
DECLARE @RecTime datetime;
DECLARE @sql nvarchar(max);
DECLARE @params nvarchar(max);
DECLARE files_cursor CURSOR FOR
SELECT ID, tblName, tblID, BinaryData
FROM @Files
-- Loop over all rows in the @Files table
OPEN files_cursor
FETCH NEXT FROM files_cursor INTO @ID, @tblName, @tblID, @BinaryData
WHILE @@FETCH_STATUS = 0
BEGIN
-- Find the referenced table row and extract its RecTime.
SET @RecTime = NULL;
SET @sql = CONCAT(
'SELECT @RecTime = RecTime FROM #', @tblName, ' WHERE ID = ', @tblID);
SET @params = '@RecTime datetime out';
EXEC SP_EXECUTESQL @sql, @params, @RecTime out;
-- Add result
INSERT INTO #results (ID, tblName, tblID, BinaryData, RecTime)
VALUES (@ID, @tblName, @tblID, @BinaryData, @RecTime);
FETCH NEXT FROM files_cursor INTO @ID, @tblName, @tblID, @BinaryData;
END
-- Finalise
CLOSE files_cursor;
DEALLOCATE files_cursor;
-- Display the results from temporary table
SELECT * FROM #results;
Run Code Online (Sandbox Code Playgroud)
在线演示: http ://rextester.com/DXCK86463
这是执行上述操作的最简单方法。不需要循环或任何东西。您需要动态代码,因为可以随时添加表。
注意:在表的示例数据中Files似乎有错误的数据tblId?
因此,我正在更改您的数据以将 ID 与相应的表相匹配。
架构:
CREATE TABLE Table1 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Table2 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Table3 (
[ID] [bigint]
, [RecTime] [datetime]
)
CREATE TABLE Files (
[ID] [bigint]
, [tblName] nvarchar(255) NULL
, [tblID] bigint NULL
, [BinaryData] varbinary(max)
/* and some other columns */
)
INSERT INTO Table1 (
[ID]
, [RecTime]
)
SELECT '1', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '2', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '3', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '4', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '5', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Table2 (
[ID]
, [RecTime]
)
SELECT '11', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '12', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '13', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '14', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '15', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Table3 (
[ID]
, [RecTime]
)
SELECT '21', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '22', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '23', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '24', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
UNION ALL SELECT '25', DATEADD(day, (ABS(CHECKSUM(NEWID())) % 65530), 0)
INSERT INTO Files (
[ID]
, [tblName]
, [tblID]
, [BinaryData]
)
SELECT '1', 'Table1', '1', 0x010203040506
UNION ALL SELECT '2', 'Table1', '2', 0x010203040506
UNION ALL SELECT '3', 'Table1', '2', 0x010203040506
UNION ALL SELECT '4', 'Table1', '3', 0x010203040506
UNION ALL SELECT '5', 'Table1', '4', 0x010203040506
UNION ALL SELECT '6', 'Table1', '5', 0x010203040506
UNION ALL SELECT '7', 'Table1', '5', 0x010203040506
UNION ALL SELECT '8', 'Table2', '11', 0x010203040506
UNION ALL SELECT '9', 'Table2', '11', 0x010203040506
UNION ALL SELECT '10', 'Table2', '12', 0x010203040506
UNION ALL SELECT '11', 'Table2', '13', 0x010203040506
UNION ALL SELECT '12', 'Table2', '14', 0x010203040506
UNION ALL SELECT '13', 'Table2', '12', 0x010203040506
UNION ALL SELECT '14', 'Table2', '15', 0x010203040506
UNION ALL SELECT '15', 'Table3', '21', 0x010203040506
UNION ALL SELECT '16', 'Table3', '22', 0x010203040506
UNION ALL SELECT '17', 'Table3', '24', 0x010203040506
UNION ALL SELECT '18', 'Table3', '23', 0x010203040506
UNION ALL SELECT '19', 'Table3', '25', 0x010203040506
UNION ALL SELECT '20', 'Table3', '25', 0x010203040506
UNION ALL SELECT '21', 'Table3', '21', 0x010203040506
Run Code Online (Sandbox Code Playgroud)
现在是您的动态查询部分:
DECLARE @QRY VARCHAR(MAX)='', @Tables VARCHAR(MAX)='';
--Capturing List of Table names for selecting RecTime
SELECT @Tables = @Tables+ tblName+'.RecTime,' FROM (
SELECT DISTINCT tblName FROM Files
)A
--To remove last comma
SELECT @Tables = SUBSTRING(@Tables,1, LEN(@Tables)-1)
--Preparing Dynamic Qry
SELECT @QRY = '
SELECT Files.ID,Files.BinaryData
,COALESCE('+@Tables+') AS RecTime
FROM Files '
SELECT @QRY =@QRY+ JOINS FROM (
SELECT DISTINCT '
LEFT JOIN '+ tblName + ' ON Files.tblID = '+tblName+'.ID AND Files.tblName= '''+tblName+''''
as JOINS
FROM Files
)A
print @QRY
EXEC( @QRY)
Run Code Online (Sandbox Code Playgroud)
如果你想看看@Qry包含什么
/*
Print Output:
SELECT Files.ID,Files.BinaryData
,COALESCE(Table1.RecTime,Table2.RecTime,Table3.RecTime) AS RecTime
FROM Files
LEFT JOIN Table1 ON Files.tblID = Table1.ID AND Files.tblName= 'Table1'
LEFT JOIN Table2 ON Files.tblID = Table2.ID AND Files.tblName= 'Table2'
LEFT JOIN Table3 ON Files.tblID = Table3.ID AND Files.tblName= 'Table3'
*/
Run Code Online (Sandbox Code Playgroud)