运行 动态 sql 基于 table 中的值,然后将它们追加到同一结果集中
Running dynamic sql based on values in a table, and then appending them to the same result set
我有一个table我需要查询名为“customData”的查询。
table 包含三列(相关列)。一个包含字段,另一个包含 table 名称,第三个包含外部记录的 ID table。目标是获取这些值,在该外部 table 中查找该字段的值,并将其作为列追加到结果集中。这可能吗?如果尚未读取 table,我无法想出一种使用 table 中的数据构建动态 sql 的方法。
我试图通过将其转换为存储过程来修复当前在未映射字段中发生的极其缓慢的查找。
ID Value foreignTable ftIndex ftRow customField
1 "yes" "tblDriver" 2001 "Name" "Licensed?"
2 "no" "tblDriver" 2002 "Name" "Licensed?"
3 "7" "tblOrigin" 1131 "Name" "tank count"
预期输出:
1 "licensed" "yes" 'Darryl Coffman'
2 "licensed" "no" 'Cash Rainer'
3 "tank count" "7" 'texas field'
虽然这并没有具体回答问题,但我想将我的上述评论总结成一个答案,以说明为什么您的设计有如此大的缺陷,以及为什么它行不通;因此,为什么你需要修复它。 请注意,除了声明您需要花费大量时间将设计修复为规范化方法之外,我们没有足够的信息来建议该修复是什么。
首先,实现最终目标的唯一方法是动态 SQL。如果你只是在一个列值之后,那么你实际上是“好的”,因为你可以写这样的语句:
SELECT DT.ID,
DT.CustomField,
DT.[Value],
D.[Name]
FROM dbo.DenormalisedTable DT
JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
WHERE DT.ftRow = N'Name';
不幸的是,您继续声明情况并非如此,这意味着您需要如下所示的查询(对于我们拥有的 2 个示例 table):
SELECT DT.ID,
DT.CustomField,
DT.[Value],
CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name] END
WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN O.[Name] END
END
FROM dbo.DenormalisedTable DT
LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
AND DT.ftIndex = O.ID;
但是,显然,它有很多其他 table,并且很可能还有其他列(不仅仅是 name
列)可以动态获取其值。所以你最终会得到这样糟糕的结果:
SELECT DT.ID,
DT.CustomField,
DT.[Value],
CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
WHEN N'Age' THEN D.Age
WHEN N'Dob' THEN D.Dob
END
WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
WHEN N'Age' THEN D.Age
WHEN N'Dob' THEN D.Dob
END
WHEN ... --20 more WHENs, 50? Plus all the inner CASE expressions
WHEN N'tblOwner' THEN CASE DT.CustomField WHEN N'FirstTraded' THEN Onr.FirstTraded
...
END
END AS ColumnValue
FROM dbo.DenormalisedTable DT
LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
AND DT.ftIndex = O.ID
LEFT JOIN ...
---20 more JOINs, 50?
LEFT JOIN dbo.tblOwner Onr ON DT.ForeignTable = N'tblOwner'
AND DT.ftIndex = Onr.ID;
然而,这有一些主要问题。第一个是数据类型的隐式转换
例如,请注意我这里有 Name
、Age
和 Dob
列。这些选择是有意为之的,因为存在完全不同类型的数据;分别为字符串、数字和日期和时间。如果一个 CASE
表达式 returns 不同的数据类型那么 Data Type Precedence 将被用来确定返回的数据类型。这很可能以日期和时间数据类型结束,这意味着您的数字 and/or (n)varchar
列将由于转换错误而导致语句失败。这意味着上述失败。尽管您 可以 CONVERT
在 THEN
中返回的每个表达式,但这会严重影响数据并且很容易导致数据被消耗或不显示想要。
这个问题也适用于 ON
子句,它 假定 每个 ID
列都是相同的数据类型(int
? ),因为具有不同的数据类型将完全打破这一点。如果不是,那么您需要对适当的数据类型使用 TRY_CONVERT
,这将我们带到下一点:Performance.
性能会糟糕。让我们不要拐弯抹角。由于不同 table 所需的绝对读取次数,这样的查询不会执行良好。幸运的是,这个 returns 数据会在几分钟内,也许几小时内完成,具体取决于您的数据库有多大。在 ON
中添加类似 TRY_CONVERT
的东西会破坏 RDBMS 使用索引进行搜索的任何(微乎其微)机会。
最后,我们有可扩展性。编写以上内容本身就是一项任务,这意味着您将 必须 使用动态 SQL。但是你在这里要解决的问题是性能问题,我也告诉过你,这个解决方案会slow,我mean 慢。动态语句不会改善这一点,而且您甚至需要首先考虑使语句起作用的考虑因素不小;所以我们甚至不要走那条路,因为它已经抛出了 window.
因此,要获得高性能查询,唯一的解决方案是修复您的设计。规范化您的数据,并且不要存储查找 table 在 table 中的位置等信息。像这样的设计,虽然对用户来说可能“看起来”很直观,但 not 缩放,not 表现良好。此类设计通常来自于将 RDBMS 视为一种编程语言,并应用相同的逻辑。 SQL 不是一种编程语言,它也不像一种语言那样工作;它所擅长的和它所擅长的完全不同。
TL;DR:您的有效设计是查询缓慢的原因。您无法解决这个问题,因为您的查询(无论是什么)不是根本原因。只有修复设计才能解决性能问题,而这正是您需要投入大量时间和资源来修复的地方。
我有一个table我需要查询名为“customData”的查询。
table 包含三列(相关列)。一个包含字段,另一个包含 table 名称,第三个包含外部记录的 ID table。目标是获取这些值,在该外部 table 中查找该字段的值,并将其作为列追加到结果集中。这可能吗?如果尚未读取 table,我无法想出一种使用 table 中的数据构建动态 sql 的方法。
我试图通过将其转换为存储过程来修复当前在未映射字段中发生的极其缓慢的查找。
ID Value foreignTable ftIndex ftRow customField
1 "yes" "tblDriver" 2001 "Name" "Licensed?"
2 "no" "tblDriver" 2002 "Name" "Licensed?"
3 "7" "tblOrigin" 1131 "Name" "tank count"
预期输出:
1 "licensed" "yes" 'Darryl Coffman'
2 "licensed" "no" 'Cash Rainer'
3 "tank count" "7" 'texas field'
虽然这并没有具体回答问题,但我想将我的上述评论总结成一个答案,以说明为什么您的设计有如此大的缺陷,以及为什么它行不通;因此,为什么你需要修复它。 请注意,除了声明您需要花费大量时间将设计修复为规范化方法之外,我们没有足够的信息来建议该修复是什么。
首先,实现最终目标的唯一方法是动态 SQL。如果你只是在一个列值之后,那么你实际上是“好的”,因为你可以写这样的语句:
SELECT DT.ID,
DT.CustomField,
DT.[Value],
D.[Name]
FROM dbo.DenormalisedTable DT
JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
WHERE DT.ftRow = N'Name';
不幸的是,您继续声明情况并非如此,这意味着您需要如下所示的查询(对于我们拥有的 2 个示例 table):
SELECT DT.ID,
DT.CustomField,
DT.[Value],
CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name] END
WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN O.[Name] END
END
FROM dbo.DenormalisedTable DT
LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
AND DT.ftIndex = O.ID;
但是,显然,它有很多其他 table,并且很可能还有其他列(不仅仅是 name
列)可以动态获取其值。所以你最终会得到这样糟糕的结果:
SELECT DT.ID,
DT.CustomField,
DT.[Value],
CASE DT.ForeignTable WHEN N'tblDriver' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
WHEN N'Age' THEN D.Age
WHEN N'Dob' THEN D.Dob
END
WHEN N'tblOrigin' THEN CASE DT.CustomField WHEN N'Name' THEN D.[Name]
WHEN N'Age' THEN D.Age
WHEN N'Dob' THEN D.Dob
END
WHEN ... --20 more WHENs, 50? Plus all the inner CASE expressions
WHEN N'tblOwner' THEN CASE DT.CustomField WHEN N'FirstTraded' THEN Onr.FirstTraded
...
END
END AS ColumnValue
FROM dbo.DenormalisedTable DT
LEFT JOIN dbo.tblDriver D ON DT.ForeignTable = N'tblDriver'
AND DT.ftIndex = D.ID
LEFT JOIN dbo.tblOrigin O ON DT.ForeignTable = N'tblOrigin'
AND DT.ftIndex = O.ID
LEFT JOIN ...
---20 more JOINs, 50?
LEFT JOIN dbo.tblOwner Onr ON DT.ForeignTable = N'tblOwner'
AND DT.ftIndex = Onr.ID;
然而,这有一些主要问题。第一个是数据类型的隐式转换
例如,请注意我这里有 Name
、Age
和 Dob
列。这些选择是有意为之的,因为存在完全不同类型的数据;分别为字符串、数字和日期和时间。如果一个 CASE
表达式 returns 不同的数据类型那么 Data Type Precedence 将被用来确定返回的数据类型。这很可能以日期和时间数据类型结束,这意味着您的数字 and/or (n)varchar
列将由于转换错误而导致语句失败。这意味着上述失败。尽管您 可以 CONVERT
在 THEN
中返回的每个表达式,但这会严重影响数据并且很容易导致数据被消耗或不显示想要。
这个问题也适用于 ON
子句,它 假定 每个 ID
列都是相同的数据类型(int
? ),因为具有不同的数据类型将完全打破这一点。如果不是,那么您需要对适当的数据类型使用 TRY_CONVERT
,这将我们带到下一点:Performance.
性能会糟糕。让我们不要拐弯抹角。由于不同 table 所需的绝对读取次数,这样的查询不会执行良好。幸运的是,这个 returns 数据会在几分钟内,也许几小时内完成,具体取决于您的数据库有多大。在 ON
中添加类似 TRY_CONVERT
的东西会破坏 RDBMS 使用索引进行搜索的任何(微乎其微)机会。
最后,我们有可扩展性。编写以上内容本身就是一项任务,这意味着您将 必须 使用动态 SQL。但是你在这里要解决的问题是性能问题,我也告诉过你,这个解决方案会slow,我mean 慢。动态语句不会改善这一点,而且您甚至需要首先考虑使语句起作用的考虑因素不小;所以我们甚至不要走那条路,因为它已经抛出了 window.
因此,要获得高性能查询,唯一的解决方案是修复您的设计。规范化您的数据,并且不要存储查找 table 在 table 中的位置等信息。像这样的设计,虽然对用户来说可能“看起来”很直观,但 not 缩放,not 表现良好。此类设计通常来自于将 RDBMS 视为一种编程语言,并应用相同的逻辑。 SQL 不是一种编程语言,它也不像一种语言那样工作;它所擅长的和它所擅长的完全不同。
TL;DR:您的有效设计是查询缓慢的原因。您无法解决这个问题,因为您的查询(无论是什么)不是根本原因。只有修复设计才能解决性能问题,而这正是您需要投入大量时间和资源来修复的地方。