首先,我想说我是数据库的初学者,请不要对我进行评判。
我从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”为空,则我不想在查询中插入搜索。
希望您能理解我的问题。非常感谢。
答案 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注入攻击原因,还可能导致您错误定义参数。简单的解决方法是使用参数。
然后将进行其他优化。