【问题标题】:Dynamically select data from a table name that is stored in a another table从存储在另一个表中的表名中动态选择数据
【发布时间】:2021-09-24 14:01:16
【问题描述】:

我是 SQL Server(版本 15)的新手,需要帮助编写 SQL。

我有一个 Invoice_Header 表,其中包含 Invoice_ID 和 Invoice_Dt 作为键:

Invoice_ID Invoice_Date
1 1/1/2021
2 1/1/2021
3 2/1/2021
4 3/1/2021

有一个 Invoice_Detail 表,其中 Invoice_ID、Invoice_Dt、Invoice_Line 作为键以及 ID 和 Table_Name 字段。

Invoice_ID Invoice_Date Invoice_Line ID Table_Name
1 1/1/2021 1 2 Food_Table
1 1/1/2021 2 1 Drink_Table
2 1/1/2021 1 1 Food_Table
2 1/1/2021 2 3 Drink_Table
3 2/1/2021 1 3 Food_Table
4 3/1/2021 1 2 Drink_Table

Food_Table 有这两个字段

ID Food_Name
1 Hamburger
2 Hot Dog
3 Pasta

Drink_Table 有这两个字段

ID Drink_Name
1 Soda
2 Milk
3 Water

我想用这些列提取数据:

Invoice Invoice_Dt Invoice_Line Order_Name
1 1/1/2021 1 Hot Dog
1 1/1/2021 2 Soda
2 1/1/2021 1 Hamburger
2 1/1/2021 2 Water
3 2/1/2021 1 Pasta
4 3/1/2021 1 Milk

因此,对于 Invoice_Detail 表中的每个发票行,它都具有 Food_Table 或 Drink_Table 的 ID 和 table_name。无论这两个表上的 ID 是什么,我都需要将 Food_Name 或 Drink_Name 作为 Order_Name。请注意,该示例仅包含这两个表。但是,它可以有多个表。因此,SQL 需要转到 invoice_detail 表上的 table_name。因此,不能硬编码 Food_Table 和 Drink_Table。

我不知道如何编写这样的 SQL,如果有人能告诉我如何编写,我将不胜感激。

谢谢

【问题讨论】:

  • 你有尝试过吗?如果没有,您为什么要研究并尝试解决问题?是否可以修复设计?作为 SQL 新手,这种设计是一个非常糟糕的选择。即使对于精通该语言或 SQL 动态 SQL 的人来说,创建一个要求使用动态 SQL 的数据库也是一个设计缺陷。
  • 数据库的设计是你的问题。你不应该有单独的表格“食物”和“饮料”。相反,您应该为所有可开票项目提供一张表格。如果您需要对单个项目进行分类,请添加一个“项目类型”字段,其中包含值“食物”和“饮料”。
  • 根据您的构建在这条路径上的扩展程度,您仍然可以很容易地避免使用动态 sql。您可以使用对每个表的条件联接编写单个查询,并且只实际执行与Invoice_Detail.Table_Name 的值匹配的表的联接。或者你可以做一些事情,比如创建一个存储过程并使用像IF Table_Name = 'Food_Table' BEGIN SELECT YourColumns FROM YourTables LEFT JOIN Food_Table ON YourConditions; END;这样的流语句来有条件地运行适当的查询。
  • @Larnu。我没有创建数据库。我只是一个试图检索数据以进行报告的用户。
  • 我真的建议与设计师讨论修复设计,如果我诚实的话,@user3241884。否则,您将不得不采用动态 SQL 方法,它不太可能具有高性能,并且除非您真正知道自己在做什么,否则您将在系统中引入大量安全漏洞。

标签: sql sql-server tsql dynamic-sql dynamic-tables


【解决方案1】:

如果只有几张表,我不会尝试用动态 SQL 做任何“聪明”的事情,也不会太花哨。最简单的方法是 LEFT OUTER JOIN 到表中并在连接条件中包含 Table_Name 的值,然后使用 COALESCE 函数返回第一个非空值(无论如何应该只有一个)。

查询是这样的:

SELECT i.Invoice_ID AS [Invoice], i.Invoice_Date AS [Invoice_Dt], 
i.Invoice_Line, COALESCE(d.Drink_Name, f.Food_Name) AS [Order_Name]
FROM Invoice_Detail AS i
LEFT OUTER JOIN Drink_Table d ON d.ID = i.ID AND i.Table_Name ='Drink_Table'
LEFT OUTER JOIN Food_Table f ON f.ID = i.ID AND i.Table_Name = 'Food_Table'

如果您想尝试一下,我创建了一个简单的脚本来声明表变量并用您的示例数据填充它们。您可以运行脚本或修改它以尝试不同的结果。如果您有更多表,则只需要更多LEFT OUTER JOINs...这可能是处理这种情况的最简单方法。

DECLARE @Invoice_Header AS TABLE (
    [Invoice_ID] INT NOT NULL, 
    [Invoice_Date] DATE NOT NULL
);
INSERT INTO @Invoice_Header(Invoice_ID,Invoice_Date)
VALUES (1, '1/1/2021'),(2, '1/1/2021'),(3,'2/1/2021'),(4,'3/1/2021');

DECLARE @Invoice_Detail AS TABLE (
    [Invoice_ID] INT NOT NULL, 
    [Invoice_Date] DATE NOT NULL, 
    [Invoice_Line] INT NOT NULL, 
    [ID] INT NOT NULL, 
    [Table_Name] VARCHAR(20) NOT NULL
);
INSERT INTO @Invoice_Detail(Invoice_ID,Invoice_Date,Invoice_Line,ID,Table_Name)
VALUES (1, '1/1/2021',1,2,'Food_Table'),(1,'1/1/2021',2,1,'Drink_Table'),
(2,'1/1/2021',1,1,'Food_Table'),(2,'1/1/2021',2,3,'Drink_Table'), 
(3,'2/1/2021',1,3,'Food_Table'),(4,'3/1/2021',1,2,'Drink_Table');

DECLARE @Food_Table AS TABLE(
    [ID] INT NOT NULL, 
    [Food_Name] VARCHAR(20) NOT NULL
);
INSERT INTO @Food_Table(ID,Food_Name)
VALUES (1,'Hamburger'),(2,'Hot Dog'),(3,'Pasta');

DECLARE @Drink_Table AS TABLE(
    [ID] INT NOT NULL, 
    [Drink_Name] VARCHAR(20) NOT NULL
);
INSERT INTO @Drink_Table(ID,Drink_Name)
VALUES (1,'Soda'),(2,'Milk'),(3,'Water');

SELECT i.Invoice_ID AS [Invoice], i.Invoice_Date AS [Invoice_Dt], 
i.Invoice_Line, COALESCE(d.Drink_Name, f.Food_Name) AS [Order_Name]
FROM @Invoice_Detail AS i
LEFT OUTER JOIN @Drink_Table d ON d.ID = i.ID AND i.Table_Name ='Drink_Table'
LEFT OUTER JOIN @Food_Table f ON f.ID = i.ID AND i.Table_Name = 'Food_Table'

您将看到预期的输出:

Invoice Invoice_Dt Invoice_Line OrderName
1 2021-01-01 1 Hot Dog
1 2021-01-01 2 Soda
2 2021-01-01 1 Hamburger
2 2021-01-01 2 Water
3 2021-02-01 1 Pasta
4 2021-03-01 1 Milk

【讨论】:

  • 谢谢。为了简化,我只举了两个表作为例子。我使用的系统(来自第三方)实际上有 37 个表并且可以增长。我知道并同意每个人的设计是糟糕的。使用两个表,我当然可以执行 select sql 或存储过程,如图所示或使用 cte。但是所有这些都需要硬编码/选择表格。如果添加了新表,则必须进行修改。我想没有硬编码或不求助于编写代码就没有办法做到这一点。
  • 由于在您的示例中,您要显示的列的名称在其他表中不同,因此没有自动的好方法。我同意,设计很糟糕,但我也知道,当你有一个 3rd 方系统时,你不会告诉他们解决他们的问题......我只是想提供一个答案,而不是对设计的一些尖刻评论你有一个合理的问题。老实说,即使有 37 个表,查询也没有那么糟糕,但鉴于它可以增长,您必须准备好维护查询。
猜你喜欢
  • 1970-01-01
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-20
  • 1970-01-01
  • 2021-12-14
  • 1970-01-01
相关资源
最近更新 更多