使用参数FASTER进行T-Sql查询

时间:2018-08-10 09:44:39

标签: sql sql-server tsql select

首先,我想说我是数据库的初学者,请不要对我进行评判。

我从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服务中的所有参数都清空了。数据库将在这些参数上花费大量时间。我想做的是一个查询,我可以只搜索不为null的参数。例如,如果“ t.total_amount”为空,则我不想在查询中插入搜索。

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

2 个答案:

答案 0 :(得分: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 <dateinici> AND <datefinal>之类的内容,则可以使用索引。

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

根据优化器,应该认识到如果值为NULL并且约束为x IS NULL OR x LIKE <something>,则比较值没有意义。

可能值得进行示例查询并查看其使用了什么执行计划?

使用现代JOIN语法,很明显没有使用您的tills表,实际上是CROSS JOIN的表,如果表{大桌子?

答案 1 :(得分:1)

您的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注入攻击原因,还可能导致您错误定义参数。简单的解决方法是使用参数。

然后将进行其他优化。