我有这个查询,当我声明变量时,性能会大大降低:
DECLARE @StartDate DATETIME,
@EndDate DATETIME
SET @StartDate = '2018-08-13'
SET @EndDate = '2018-08-19'
SELECT *
FROM [DIME_WH].[dbo].[FactOrderLines2] FL (nolock)
WHERE DD_OrderDate >= @StartDate
AND DD_OrderDate <= @EndDate
这比此SQL语句慢得多:
SELECT *
FROM [DIME_WH].[dbo].[FactOrderLines2] FL (nolock)
WHERE DD_OrderDate >= '2018-08-01'
AND DD_OrderDate <= '2018-08-17'
两个查询最后都会返回相同的结果。
答案 0 :(得分:3)
$mysqli = new mysqli("a", "a", "a", "a");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$sql = "UPDATE laporan_gini SET id_provinsi='1' WHERE nama_item_vertical_variabel= 'INDONESIA'";
$sql = "UPDATE laporan_gini SET id_provinsi='61' WHERE nama_item_vertical_variabel= 'KALIMANTAN BARAT'";
if (!$mysqli->multi_query($sql)) {
echo "Multi query failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
do {
if ($res = $mysqli->store_result()) {
var_dump($res->fetch_all(MYSQLI_ASSOC));
$res->free();
}
} while ($mysqli->more_results() && $mysqli->next_result());
在参数中使用常量时,SELECT * FROM [DIME_WH].[dbo].[FactOrderLines2] FL (nolock)
WHERE DD_OrderDate >= '2018-08-01'
AND DD_OrderDate <= '2018-08-17'
为此查询创建Optimiser
。因此,如果以相同的值执行相同的查询,则计划被重用,如果值被更改,则创建另一个计划。
所以具有恒定值的参数很快。
special plan
在参数中使用变量时。然后优化器为传递的第一个参数值创建执行计划。
例如,SELECT *
FROM [DIME_WH].[dbo].[FactOrderLines2] FL (nolock)
WHERE DD_OrderDate >= @StartDate
AND DD_OrderDate <= @EndDate
和@StartDate='2018-08-01'
的值是第一次通过。
然后,由优化程序创建最佳执行计划。这个计划足以满足这个价值。
下次通过@EndDate='2018-08-07'
和@StartDate='2018-08-01'
值时,则使用相同的先前计划,这对于该参数可能不是最佳选择。
换句话说,对于第一个值而言最优的相同计划对于另一个值而言是次优的。
所以查询的执行效果可能会很差且很慢。这称为@EndDate='2018-08-31'
。
有几种方法可以解决此问题。
注意::在此主题中,我们仅关注为何变量性能较慢而其他因素保持不变的原因。
答案 1 :(得分:1)
这是因为SQL Server在优化时不知道变量的值-当它做出估计并且无法为其查找任何statistics时(作为一种可能性),因此它(很可能是) )仅扫描整个表格,而不是进行查找(搜索)。
如果在存储过程内使用或用sniffed参数化,则它们可以是“ sp_executesql”
答案 2 :(得分:0)
问题可能是参数嗅探,也许不是。由于@KumarHarsh已经介绍了该主题,因此我将跳过该主题。这里最重要的问题是: FactOrderLines.DD_OrderDate是什么数据类型? 。出于性能原因以及正确性,这很重要。
性能第一。如果DD_OrderDate是DATE
数据类型,而您的变量或参数是DATETIME,则优化器必须跳过多余的圈来利用您的索引,否则将被迫进行扫描而不是查找。请注意以下示例数据:
USE tempdb;
GO
IF OBJECT_ID('#FactOrderLines') IS NOT NULL DROP TABLE #FactOrderLines;
GO
CREATE TABLE #FactOrderLines(someId INT IDENTITY, DD_OrderDate DATETIME NOT NULL);
CREATE CLUSTERED INDEX nc_factOrderLines ON #FactOrderLines(DD_OrderDate);
INSERT #FactOrderLines(DD_OrderDate)
SELECT TOP (10000) DATEADD(DAY,CHECKSUM(NEWID())%100, getdate())
FROM sys.all_columns;
GO
现在让我们比较以下查询的执行计划:
-- AS DATE
DECLARE @StartDate DATE = '2018-08-01',
@EndDate DATE = '2018-08-20';
SELECT *
FROM #FactOrderLines
WHERE DD_OrderDate >= @StartDate
AND DD_OrderDate <= @EndDate
OPTION (RECOMPILE)
GO
-- AS DATETIME
DECLARE @StartDate DATETIME = '2018-08-01',
@EndDate DATETIME = '2018-08-31';
SELECT *
FROM #FactOrderLines
WHERE DD_OrderDate >= @StartDate
AND DD_OrderDate <= @EndDate
OPTION (RECOMPILE);
执行计划:
由于这个原因-您要确保变量/参数使用的数据类型与它们要处理的列相同。
现在讲正确性;注意此查询:
DECLARE @StartDate DATE = '2018-08-01',
@EndDate DATE = '2018-08-20';
SELECT
[getdate as datetime] = GETDATE(),
[@enddate as datetime] = CAST(@EndDate AS DATETIME),
[getdate as date] = CAST(GETDATE() AS DATE),
[datetime equality] = IIF(GETDATE() > @EndDate,'yep','nope'),
[date equality] = IIF(CAST(GETDATE() AS DATE) > @EndDate,'yep','nope');
结果:
getdate as datetime @enddate as datetime getdate as date datetime equality date equality
----------------------- ----------------------- --------------- ----------------- -------------
2018-08-20 13:52:46.247 2018-08-20 00:00:00.000 2018-08-20 yep nope
日期格式的值转换为日期时间为0小时0秒...