Ι有一些表格(例如。[Table1]
,[Table2]
,[Table3]
等),其中[ID]
为主键,RecTime
为{每个{1}}。
ΑlsoΙ有一个表DATETIME
,用于保存[Files]
列中的文件,并引用具有其名称和ID的其他表。
varbinary(max)
,[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
表加入到其他表中,[Files]
和Name
从“[Files]”表中的值派生?
我需要来自ID
表的[BinaryData]
和来自[Files]
表中相应表格引用的[RecTime]
。
真正的问题是,[Files]
,[Table1]
和[Table2]
不是唯一被称为[Table3]
表的表。可以创建新表,二进制数据必须存储在[Files]
表中。
所以我正在寻找一种动态“加入”它们的方法。
P.S。我不是这个系统的创造者,也不能对它进行任何结构性改变,只是试图解决这个问题。
任何帮助都将不胜感激。
答案 0 :(得分:7)
一种方法是创建一个包含所有表数据的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)'
你从中获得的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)
执行它:
EXEC(@SQL)
结果:
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
答案 1 :(得分:5)
一种解决方案是使用cursor为@Files
表格中的每一行执行一些dynamic SQL:
-- 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;
答案 2 :(得分:2)
这是最简单的方法来执行上述操作。不需要循环或任何东西。您需要动态代码,因为可以随时添加表格。
注意:在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
现在您的动态查询部分:
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)
如果您想查看@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'
*/
答案 3 :(得分:1)
此设计只是在ER中建模层次结构的一种方法。您基本上有一个基于表名的物理分区表(即Table1
,Table2
等等)。
因此,加入这些表的最简单方法是创建分区视图,然后加入它。
就你的例子而言,你只需要这样做:
CREATE VIEW vmAll AS
SELECT 'Table1' AS 'tblName', [ID], [RecTime] FROM Table1
UNION ALL
SELECT 'Table2' AS 'tblName', [ID], [RecTime] FROM Table2
UNION ALL
SELECT 'Table3' AS 'tblName', [ID], [RecTime] FROM Table3;
GO
现在像往常一样加入Files
表(记得也指定分区字段):
例如:
SELECT
F.[ID]
, F.[tblName]
, F.[tblID]
, F.[BinaryData]
, A.RecTime
FROM [Files] F
LEFT OUTER JOIN vmAll A ON
F.[ID] = A.[ID] AND
F.tblName = A.tblName
给出预期结果:
注意一件重要的事情:因为它是一个分区视图,SQL Server能够执行分区消除,从而大大加快了连接速度(正确的术语应该是 table elimination < / em>的)。
例如,之前的执行计划是:
如果我们在分区列上添加过滤谓词:
SELECT
F.[ID]
, F.[tblName]
, F.[tblID]
, F.[BinaryData]
, A.RecTime
FROM [Files] F
LEFT OUTER JOIN vmAll A ON
F.[ID] = A.[ID] AND
F.tblName = A.tblName
WHERE A.tblName = 'Table1'
我们将获得此执行计划(请注意根本不扫描两个表):
当然,为了使用分区视图,您必须首先创建它。您可以通过编程方式查找具有以下查询的特定字段:
;WITH CTE AS
(
SELECT C.object_id FROM sys.columns C
INNER JOIN sys.objects O ON C.object_id = O.object_id
WHERE
(C.[name] = 'ID' OR C.[name] = 'RecTime')
AND O.[type] = 'U'
GROUP BY C.object_id
HAVING COUNT(*) = 2
)
SELECT OBJECT_NAME(object_id), object_id FROM CTE;
答案 4 :(得分:0)
请尝试以下操作。
Select res.* , F.* From Files F
Left join
(
Select 'table1' as tablename, a.* From table1 a
Union
Select 'table2' as tablename, b.* From table2 b
Union
Select 'table3' as tablename, c.* From table3 c
)Res
On res.tablename = F.tblname
答案 5 :(得分:0)
如果你只有很少的表,那么你可以这样做,它可能会稍快一点,因为它避免了动态SQL。
如果你不能告诉我们有多少桌子或者会有太多的桌子,请看看其他解决方案(我喜欢史蒂夫室的解决方案)。
SELECT F.*, RecTime =
CASE tblName
WHEN 'Table1' THEN COALESCE(T1.RecTime, NULL)
WHEN 'Table2' THEN COALESCE(T2.RecTime, NULL)
WHEN 'Table3' THEN COALESCE(T3.RecTime, NULL)
ELSE NULL
END
FROM @Files F
LEFT JOIN @Table1 T1 ON F.tblID = T1.ID
LEFT JOIN @Table2 T2 ON F.tblID = T2.ID
LEFT JOIN @Table3 T3 ON F.tblID = T3.ID
答案 6 :(得分:0)