【问题标题】:How do I use variables in a select query?如何在选择查询中使用变量?
【发布时间】:2016-08-20 15:40:15
【问题描述】:

我有以下选择查询,它使用标量函数来获取全名。我想通过使用变量来消除冗余,但到目前为止还没有成功。我的查询是

select 
 a.Id, 
 a.UserName, 
 getFullName(a.UserName),
 a.CreateTime
from DataTable;

我不想检索“a.User”两次。如果我可以将 a.User 保存在一个变量中,然后将其传递给函数从而提高效率,我会更喜欢。

目前我想出的工作如下

select 
 Id, 
 UserName, 
 getFullName(UserName), 
 CreateTime
from (select a.Id, a.UserName, a.CreateTime from DataTable) temp

这解决了性能问题,但增加了两次写入相同选择的开销。任何其他建议都会很棒。

DataTable 是这样的

+----+----------+------------+
| Id | UserName | CreateTime |
+----+----------+------------+
|  1 | ab       | 10:00      |
|  2 | cd       | 11:00      |
|  3 | ef       | 12:00      |
+----+----------+------------+

这是用于获取全名的 NamesTable

+----------+----------+
| UserName | FullName |
+----------+----------+
| ab       | Aa BB    |
| cd       | Cc Dd    |
| ef       | Ee Ff    |
+----------+----------+

这是获取全名的函数

Create function [dbo].[getFullName](@user varchar(150)) returns varchar(500)
as
begin
    declare @Result varchar(500);

  select @Result = FullName from dbo.NamesTable where UserName =  @user;
  return @Result;
end;

【问题讨论】:

  • 这里真正的性能问题是标量函数的存在。他们是出了名的表现不佳。然后,当您将其粘贴在列中时,情况会变得更糟。也许我们可以帮助您创建一个内联表值函数。它将更加灵活,性能更好。我们只需要一些关于它的作用和它使用的表结构的细节。 spaghettidba.com/2015/04/24/…
  • 嗯? “解决方法”与原始查询有何不同?
  • @GordonLinoff 在前面的查询中,数据列被重复选择,但在后面的查询中,它是对投影数据的选择,因此速度更快。
  • @fredzyadi 你有没有测量它更快,或者你只是假设它是因为似乎少了一个操作?您是否发现仅在一个表达式中选择字段与在两个表达式中选择字段之间存在显着的性能差异?
  • @DStanley 我已经检查了我的完整查询。一用二用,一用操作。第一个每次要多花费 3 秒,大约需要 3000 行来获取。我有很多 XPath 操作。可能是标量函数与列作为参数一起导致延迟。但肯定存在性能问题。

标签: sql sql-server select subquery


【解决方案1】:

您正在解决一个不存在的问题。你似乎认为

select 
 a.Id, 
 a.UserName, 
 getFullName(a.UserName),
 a.CreateTime
from DataTable;

背后有一些相对昂贵的过程来获得UserName,这种过程发生了两次。实际上,一旦找到记录,获取 UserName 值几乎是一个即时过程,因为它可能会被 SQL 引擎在后台存储在“变量”中。您应该在该查询和

之间几乎没有性能差异
select 
 a.Id, 
 getFullName(a.UserName),
 a.CreateTime
from DataTable;

标量函数本身可能存在性能问题,但这并不是因为您“拉”了UserName值“两次”。

更好的方法是加入另一个表:

select 
 a.Id, 
 a.UserName, 
 b.FullName,
 a.CreateTime
from DataTable a
LEFT JOIN dbo.NamesTable b
  ON a.UserName = b.UserName

【讨论】:

  • 我无法加入它,因为该表已与另一个表加入。你能回答提出的问题吗?有没有办法可以将列值存储在变量中并在选择查询中使用它?或者你认为这是不可能的。
  • 不,不可能将列值“存储”在“变量”中并按照您希望的方式在函数调用中重用它。可能有其他种方法可以提高性能,但您没有提供足够的信息来提出有意义的建议。
  • "我无法加入它,因为该表已与另一个表连接。"不确定您的意思 - 您可以加入多个表。
  • 我有三张桌子。我的 DataTable 包含大量信息,其中包含多个具有用户名的列(例如,committedByUserName、reviewByUserName 和其他一些)。我不能用 NamesTable 加入表 A 的一个原因是每行没有一个用户名。一行中可能有 10 个不同的用户名。所以我不知道如何按照你的建议加入它。
  • 你可以多次JOIN同一张表,只要你给每个表一个别名。
【解决方案2】:

正如 D Stanley 所说,您正在尝试解决一些不存在的问题。我还要补充一点,您根本不应该使用该功能。 SQL 旨在执行基于集合的操作。当您使用这样的功能时,您现在正在让它为每一行一遍又一遍地执行相同的功能 - 这是一种可怕的做法。相反,只需在另一个表中 JOIN(基于集合的操作)并让 SQL 做它最擅长的事情:

SELECT
    DT.Id,
    DT.UserName,
    NT.fullname,
    DT.CreateTime
FROM
    DataTable DT
INNER JOIN NamesTable NT ON NT.username = DT.username;

另外,DataTableNamesTable 是用于表的可怕名称。当然它们是表格,所以没有必要在名称的末尾加上“表格”。此外,当然第一个包含“数据”,它是一个数据库。您的表名应该是描述性的。 DataTable究竟有什么作用?

如果您将来要进行 SQL 开发,那么我强烈建议您阅读几本关于该主题的介绍性书籍,并观看尽可能多的教程视频。

【讨论】:

  • 这些名称仅用于示例。我不愿意在这里用真实的表名和真实的表数据发布我的实际查询。我认为该示例可以很好地达到目的,如果您仍然无法回答所提出的问题,那么请确保您可以继续观看尽可能多的教程;)
  • 该建议不仅仅基于命名约定。从您的问题中可以明显看出您不了解关系数据库背后的一些基本概念。这并不是对个人的侮辱,如果看起来是这样,我很抱歉。作为顾问修复代码,我赚了很多钱,就像你写的那样,因为开发人员认为他们可以安装 SQL 并开始编码。我永远不会用完工作,所以我希望更多的开发人员完全避免这些问题。
  • 如何“明显”。好吧,我会接受你的话道歉,而且我真的不在乎你赚了多少钱,因为我现在只关心我的问题的答案。但是,我感谢您对我的关注以及您为这个社区所做的贡献。谢谢。
【解决方案3】:

Scalar UDF 将针对每一行执行,但绝对不是您想的那样。下面是示例演示和执行计划,证明是相同的..

create table testid
(
id int,
name varchar(20)
)


insert into testid
select n,'abc'
from numbers
where n<=1000000

create index nci_get on dbo.testid(id,name)

select id,name,dbo.getusername(id) from dbo.testid where id>4

以下是上述查询的执行计划

解码上述方案: 索引搜索输出 id,name
然后计算标量尝试从现有行值计算新行。在这种情况下 expr1003 是我们的函数

索引查找成本为 97%,计算标量成本为 3%,并且您可能知道索引查找不是去表获取数据的运算符。所以希望这可以解决您的问题

【讨论】:

    猜你喜欢
    • 2012-11-25
    • 2023-03-28
    • 1970-01-01
    • 2012-01-22
    • 2021-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多