【问题标题】:Make T-Sql query with parameters FASTER使用参数更快地进行 T-Sql 查询
【发布时间】:2018-08-10 09:44:38
【问题描述】:

首先我想说我真的是数据库初学者,不要评判我。

我从 web 服务接收到很多参数,我必须使用这些参数进行 sql 查询。问题是我的查询消耗了大量的 cpu,因为我对查询的优化不好。

  SELECT 
  SUM(t.total_amount) as SumaAmount,
  COUNT(t.id)  as TotalTransaccions

    FROM RealTimeVending.dbo.rtv_turnover_transaction as t, 
    RealTimeVending.dbo.rtv_trans_articles as ta,
    RealTimeVending.dbo.articles as art, RealTimeVending.dbo.groups,
    RealTimeVending.dbo.Clients as s,
    RealTimeVending.dbo.rtv_transactions as tr, 
    RealTimeVending.dbo.tills as till, RealTimeVending.dbo.Ubicacion as u, 
    RealTimeVending.dbo.Operadores as o 

    where t.operador_id=o.ID and t.transaction_id=ta.transaction_id and 
    art.id=ta.article_id and s.id=t.cliente_id and tr.id=t.transaction_id 
    and groups.id=art.group_a_id and t.ubicacio_id=u.id 

    and convert(date,t.trans_date) >='"+globalMap.get("datainici")+"'and 
    convert(date,t.trans_date) <= '"+globalMap.get("datafinal")+"'and 
    and (s.codigo IS NULL or s.codigo like '%"+globalMap.get("client")+"%') 
    and (t.total_amount IS NULL or t.total_amount like'"+globalMap.get("amount")+"%') 

想象一下,我将 web 服务中的所有参数都设为空。数据库将花费大量时间处理这些参数。我想做的是一个查询,我可以在其中搜索不为空的参数。例如,如果“t.total_amount”为空,我不想在查询中插入搜索。

我希望你能理解我的问题。非常感谢。

【问题讨论】:

  • 作为初学者无需道歉——除了查询、表的模式、表上的索引,理想情况下,任何人都需要查询的查询计划提供具体建议。即使没有这些,s.codigo like '%"+globalMap.get("client")+"%' 也会导致问题,像这样的完整通配符搜索将始终扫描数据
  • 今日提示:切换到现代、明确的JOIN 语法!更容易编写(没有错误),更容易阅读和维护,如果需要更容易转换为外连接。
  • 此查询使用带有字符串连接的变量而不是参数。我无法评论如何使用talend 正确参数化它,但查询将包含参数标记,其中实际运行时值分别添加到命令中(例如convert(date,t.trans_date) &gt;= ?)。最好重构查询以避免将convert 之类的函数应用于列,以便有效地使用索引。

标签: sql sql-server tsql select


【解决方案1】:

这里快速重写您的查询以使用现代 JOIN 语法:

SELECT 
  SUM(t.total_amount) AS SumaAmount,
  COUNT(t.id) AS TotalTransaccions
FROM 
    RealTimeVending.dbo.rtv_turnover_transaction t
    INNER JOIN RealTimeVending.dbo.rtv_trans_articles ta ON ta.transaction_id = t.transaction_id
    INNER JOIN RealTimeVending.dbo.articles art ON art.id = ta.article_id
    INNER JOIN RealTimeVending.dbo.groups g ON g.id = art.group_id
    INNER JOIN RealTimeVending.dbo.Clients s ON s.id = t.cliente_id
    INNER JOIN RealTimeVending].dbo.rtv_transactions tr ON tr.id = t.transaction_id
    --INNER JOIN RealTimeVending.dbo.tills till (not used)
    INNER JOIN RealTimeVending.dbo.Ubicacion u ON u.id = t.ubicacio_id 
    INNER JOIN RealTimeVending.dbo.Operadores o ON o.id = t.operador_id
WHERE
    CONVERT(DATE, t.trans_date) >= '"+globalMap.get("datainici")+"' --is this really not already a date?
    AND CONVERT(DATE, t.trans_date) <= '"+globalMap.get("datafinal")+"'
    AND (s.codigo IS NULL or s.codigo LIKE '%"+globalMap.get("client")+"%') 
    AND (t.total_amount IS NULL or t.total_amount LIKE '"+globalMap.get("amount")+"%');

让我印象深刻的第一件事是,您的 trans_date 正在转换为 DATE 以进行比较,这可能效率低下。如果您可以将此标准更改为 t.trans_date BETWEEN &lt;dateinici&gt; AND &lt;datefinal&gt; 之类的内容会更好,因为这将允许使用索引。

您确实应该正确使用参数,因为这有助于避免 SQL 注入。

根据优化器的不同,它应该意识到如果它是NULL 并且您有x IS NULL OR x LIKE &lt;something&gt; 的约束,那么比较一个值是没有意义的。

可能值得进行一个示例查询并查看它使用的执行计划?

使用现代JOIN 语法,很明显您的tills 表没有被使用,所以本质上是一个CROSS JOIN,如果它是一个大表,它会减慢一切吗?

【讨论】:

    【解决方案2】:

    您的 SQL 需要大量修正。

    让我们先稍微剖析一下你的 SQL:

    FROM RealTimeVending.dbo.rtv_turnover_transaction as t, 
    RealTimeVending.dbo.rtv_trans_articles as ta,
    RealTimeVending.dbo.articles as art, RealTimeVending.dbo.groups,
    RealTimeVending.dbo.Clients as s,
    RealTimeVending].dbo.rtv_transactions as tr, 
    RealTimeVending.dbo.tills as till, RealTimeVending.dbo.Ubicacion as u, 
    RealTimeVending.dbo.Operadores as o 
    where t.operador_id=o.ID and t.transaction_id=ta.transaction_id and 
    art.id=ta.article_id and s.id=t.cliente_id and tr.id=t.transaction_id 
    and groups.id=art.group_a_id and t.ubicacio_id=u.id 
    and convert(date,t.trans_date) >='"+globalMap.get("datainici")+"'and 
    convert(date,t.trans_date) <= '"+globalMap.get("datafinal")+"'and 
    and (s.codigo IS NULL or s.codigo like '%"+globalMap.get("client")+"%') 
    and (t.total_amount IS NULL or t.total_amount like'"+globalMap.get("amount")+"%') 
    

    您使用的是旧式连接,这使得它更难阅读。您可以将其重写为(假设 RealTimeVending 是当前数据库):

    FROM rtv_turnover_transaction as t
    inner join rtv_trans_articles as ta on t.transaction_id=ta.transaction_id 
    inner join articles as art on art.id=ta.article_id
    inner join groups on groups.id=art.group_a_id
    inner join Clients as s on s.id=t.cliente_id
    inner join rtv_transactions as tr on tr.id=t.transaction_id  
    cross join tills as till 
    inner join Ubicacion as u on t.ubicacio_id=u.id
    inner join Operadores as o on t.operador_id=o.ID
    
    where
    --convert(date,t.trans_date) >='"+globalMap.get("datainici")+"'and 
    --convert(date,t.trans_date) <= '"+globalMap.get("datafinal")+"'and 
    --and (s.codigo IS NULL or s.codigo like '%"+globalMap.get("client")+"%') 
    --and (t.total_amount IS NULL or t.total_amount like'"+globalMap.get("amount")+"%') 
    

    这里有许多表的内部连接和一个完全(不仅)无用的 CROSS JOIN。交叉连接本身就是一个缓慢的原因。使用交叉连接,例如:

    t1 cross join t2
    

    如果 t1 有 1000 行,而 t2 有 10000 行,您将得到 1000 * 10000 = 10,000,000 行,其中 t1 上的每一行重复 10,000 次(而 t2 行 1,000 次)。

    下一个问题是当您只需要来自 rtv_turnover_transaction 的字段时,为什么要加入其他表?如果不知道模式和必需品,可能故意无法知道。可能它们仅用于(如果存在)检查。如果是这样,您可以将它们添加为 WHERE 中的 EXISTS 查询。

    然后是你的 WHERE 子句:

    where
     convert(date,t.trans_date) >='"+globalMap.get("datainici")+"'and 
     convert(date,t.trans_date) <= '"+globalMap.get("datafinal")+"'and 
     and (s.codigo IS NULL or s.codigo like '%"+globalMap.get("client")+"%') 
     and (t.total_amount IS NULL or t.total_amount like'"+globalMap.get("amount")+"%') 
    

    这就是为什么:

    convert(date,t.trans_date)
    

    这将使 trans_date 上现有索引的使用无效并使其变慢。而是:

     t.trans_date >='"+globalMap.get("datainici")+"'and
    

    如果 globalMap.get("datainici") 返回一个日期或时间部分为 00:00:00 的日期时间,则很好。

    接下来是最重要的一个:

    where
     convert(date,t.trans_date) >='"+globalMap.get("datainici")+"'and 
     convert(date,t.trans_date) <= '"+globalMap.get("datafinal")+"'and 
     and (s.codigo IS NULL or s.codigo like '%"+globalMap.get("client")+"%') 
     and (t.total_amount IS NULL or t.total_amount like'"+globalMap.get("amount")+"%') 
    

    您绝不应该通过串联字符串来构建这样的 SQL 查询。这不仅是众所周知的 SQL 注入攻击原因,还可能导致您定义错误的参数。简单的解决方法是使用 PARAMETERS。

    然后会有其他优化。

    【讨论】:

      猜你喜欢
      • 2011-06-04
      • 2022-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多