我有一个查询,通常需要五到六分钟才能运行。它有一个WHERE
和ORDER BY
子句,但没有JOIN
。但是,如果我将查询简化为:
SELECT * FROM ReportIndex
运行仍然需要五到六分钟。
该表有超过一千一百万行。
由于没有联接,因此无法看到索引如何使其更快。有人可以建议我尝试加快此查询速度的其他方法吗?
更新:
这是我正在使用的实际查询。它返回6,668,324行。
SELECT '1' AS vrsID,
lprKey,
lprRptName,
lprTitle,
lprDate,
lprOwner,
lprUserID,
lprArchiveDate,
lprTrackDate,
lprActionView,
lprActionEmail,
lprActionExcel,
lprActionForward,
lprActionReassign,
lprActionDownload,
lprActionLocalPrint,
lprActionServerPrint,
lprPageCount,
lprBytes,
lprDataType,
lprArchived,
lprJobName,
lprViewed,
lprRptID
FROM ReportIndex
WHERE (lprOwner IN ('admin', 'APAdmin', 'APClerk', 'AP-Tab-700', 'AP-Tab-A-B', 'AP-Tab-A-K_EMP', 'AP-Tab-AP520', 'AP-Tab-CAN', 'AP-Tab-C-E', 'AP-Tab-COMM', 'AP-Tab-Confidential', 'AP-Tab-EE-Waiting', 'AP-Tab-F-O', 'AP-Tab-Historical', 'AP-Tab-LCD', 'AP-Tab-LCD_EMP', 'AP-Tab-LEA', 'AP-Tab-LPP', 'AP-Tab-LPS', 'AP-Tab-LPS_EMP', 'AP-Tab-LSI', 'AP-Tab-LTI', 'AP-Tab-L-Z_EMP', 'AP-Tab-P-R', 'AP-Tab-S-Z', 'AP-Tab-Unknown', 'Group-Category-VendorDocuments', 'Group-Quality-Control', 'Group-VendorDocs', 'HRAdmin', 'HR-Category-Payroll', 'HR-Category-Performance', 'HR-Category-Personnel', 'HR-Category-Upload', 'HR-Document-Delete', 'HR-Document-Index', 'HR-Group-DocumentMaintenance', 'HR-IndexQueue-Email', 'HRROLE01', 'HRROLE02', 'HRROLE03', 'HRROLE04', 'HRROLE05', 'HRROLE06', 'HRROLE09', 'HRROLE10', 'HRROLE11', 'HRROLE12', 'HRROLE13', 'HRROLE14', 'HRROLE15', 'HRROLE16', 'HRROLE17', 'HRROLE18', 'HRROLE19', 'HRROLE21', 'HRROLE23', 'HRROLE24', 'HRROLE25', 'HRROLE26', 'HRROLE28', 'HRROLE29', 'HRROLE30', 'HRROLE31', 'HRROLE34', 'HRROLE35', 'HRROLE36', 'HRROLE37', 'HRROLE39', 'HRROLE41', 'HRROLE42', 'HRROLE43', 'HRROLE44', 'HRROLE45', 'HRROLE46', 'HRROLE47', 'HRROLE48', 'HRROLE49', 'HRROLE50', 'HRROLE51', 'HRROLE52', 'HRROLE53', 'HRROLE54', 'HRROLE55', 'HRROLE56', 'HRROLE57', 'HRROLE58', 'HRROLE59', 'HRROLE60', 'HRROLE61', 'HRROLE62', 'HRROLE63', 'WFAdmin', 'AccountsPayable')
AND lprArchived = 0 AND lprPendingDelete = 0) AND lprDone=0
ORDER BY lprDate DESC
更新2:
答案 0 :(得分:1)
这个评论太长了。
您的查询本质上是:
SELECT . . .
FROM ReportIndex
WHERE lprOwner IN (<big list>) AND
lprArchived = 0 AND
lprPendingDelete = 0 AND
lprDone = 0
ORDER BY lprDate DESC
标准建议是像ReportIndex(lprArchived, lprPendingDelete, lprDone, lprOwner, lprDate)
之类的索引。
但是,您的查询正在从1200万行中选择660万行。索引无济于事,因为SELECT
的选择性不够。如果您选择6.6万行,那么索引可能会大有帮助。
您无能为力。如果这确实很重要,则可以在(lprDate)
上创建集群索引。创建将花费一些时间-所有数据都必须在数据页上排序。
然后,查询应依次读取数据并应用WHERE
条件。
答案 1 :(得分:0)
出于效率考虑,请尽量避免使用IN子句。如果IN中包含大量的值,则速度可能会很慢。为了优化其通常的优势,可以将这些值添加到TVP,然后加入其中。然后,您还可以添加索引,以使连接更有效。尝试以下操作:
DECLARE @tvp_lprOwner AS TABLE (owner NVARCHAR(255) NOT NULL);
INSERT INTO @tvp_lprOwner (owner)
VALUES ('admin') , ('APAdmin') , ('APClerk') , ('AP-Tab-700') , ('AP-Tab-A-B') , ('AP-Tab-A-K_EMP') , ('AP-Tab-AP520') , ('AP-Tab-CAN') , ('AP-Tab-C-E') , ('AP-Tab-COMM') , ('AP-Tab-Confidential') ,
('AP-Tab-EE-Waiting') , ('AP-Tab-F-O') , ('AP-Tab-Historical') , ('AP-Tab-LCD') , ('AP-Tab-LCD_EMP') , ('AP-Tab-LEA') , ('AP-Tab-LPP') , ('AP-Tab-LPS') , ('AP-Tab-LPS_EMP') , ('AP-Tab-LSI') ,
('AP-Tab-LTI') , ('AP-Tab-L-Z_EMP') , ('AP-Tab-P-R') , ('AP-Tab-S-Z') , ('AP-Tab-Unknown') , ('Group-Category-VendorDocuments') , ('Group-Quality-Control') , ('Group-VendorDocs') , ('HRAdmin') ,
('HR-Category-Payroll') , ('HR-Category-Performance') , ('HR-Category-Personnel') , ('HR-Category-Upload') , ('HR-Document-Delete') , ('HR-Document-Index') , ('HR-Group-DocumentMaintenance') ,
('HR-IndexQueue-Email') , ('HRROLE01') , ('HRROLE02') , ('HRROLE03') , ('HRROLE04') , ('HRROLE05') , ('HRROLE06') , ('HRROLE09') , ('HRROLE10') , ('HRROLE11') , ('HRROLE12') , ('HRROLE13') ,
('HRROLE14') , ('HRROLE15') , ('HRROLE16') , ('HRROLE17') , ('HRROLE18') , ('HRROLE19') , ('HRROLE21') , ('HRROLE23') , ('HRROLE24') , ('HRROLE25') , ('HRROLE26') , ('HRROLE28') , ('HRROLE29') ,
('HRROLE30') , ('HRROLE31') , ('HRROLE34') , ('HRROLE35') , ('HRROLE36') , ('HRROLE37') , ('HRROLE39') , ('HRROLE41') , ('HRROLE42') , ('HRROLE43') , ('HRROLE44') , ('HRROLE45') , ('HRROLE46') ,
('HRROLE47') , ('HRROLE48') , ('HRROLE49') , ('HRROLE50') , ('HRROLE51') , ('HRROLE52') , ('HRROLE53') , ('HRROLE54') , ('HRROLE55') , ('HRROLE56') , ('HRROLE57') , ('HRROLE58') , ('HRROLE59') ,
('HRROLE60') , ('HRROLE61') , ('HRROLE62') , ('HRROLE63') , ('WFAdmin') , ('AccountsPayable');
SELECT '1' AS vrsID
, RI.lprKey
, RI.lprRptName
, RI.lprTitle
, RI.lprDate
, RI.lprOwner
, RI.lprUserID
, RI.lprArchiveDate
, RI.lprTrackDate
, RI.lprActionView
, RI.lprActionEmail
, RI.lprActionExcel
, RI.lprActionForward
, RI.lprActionReassign
, RI.lprActionDownload
, RI.lprActionLocalPrint
, RI.lprActionServerPrint
, RI.lprPageCount
, RI.lprBytes
, RI.lprDataType
, RI.lprArchived
, RI.lprJobName
, RI.lprViewed
, RI.lprRptID
FROM ReportIndex RI
JOIN @tvp_lprOwner O ON RI.lprOwner = O.owner
WHERE (lprArchived = 0 AND lprPendingDelete = 0)
AND lprDone = 0
ORDER BY lprDate DESC;
值得运行它,然后查看执行计划,并在需要时从那里进行索引。
答案 2 :(得分:-1)
尝试创建新索引:
$bid->save(); //when you call the save function automatically updates the inserted id.
$data['status'] = 'ok';
$data['bid'] = Bid::findOrFail($bid->id); //this will refresh the element so everything is passed correctly.
return response()->json($data);
之所以包含这5个字段,是因为其中的4个出现在您的WHERE子句中,而最后一个则用于您的ORDER BY。
根据您的用例,可能值得看一下分页结果。这可以帮助您将大容量数据传输到问题的核心。
答案 3 :(得分:-1)
根据您对评论部分的回答。
您提到表上没有索引,而且,这可能不是唯一的问题,硬件也将在其中发挥重要作用。
因此,您需要做的是首先检查硬件规格,是否过时或无法处理大量数据,然后停在那里,并建议他们使用适当的硬件进行升级。 如果它们具有良好的硬件,则可以继续进行其他SQL Server建议。
首先,数据顺序:
我在您的查询中看到您正在按lprDate降序对数据进行排序,这将是查询中昂贵的一部分,尤其是大规模查询时。实际的示例是使用不带where子句的相同查询,只使用ORDER BY lprDate DESC
,然后将其与不带where子句的相同查询进行比较(使用TOP 10000
进行测试)。您将看到两个查询之间的性能差异。如果您总是从表中选择最近的数据(大多数人都这样做),则需要创建一个降序的聚簇索引。这将重新使用表中的所有数据,并且新数据将始终位于首页。您可能会遇到的唯一问题是诉诸一个11M行将花费太多时间来处理,有时该过程会失败。因此,我实际上所做的(避免这种情况)是使用不同的名称重新创建同一张表。然后使用适当的排序以及所需的适当索引创建一个新的聚集索引。然后,当我完成后,我只是分批重新插入记录。您可以执行相同的方法,可以使用lprDate或创建一个IDENTITY(或使用当前的IDENTITY)使它们聚类并将排序调整为DESC,然后添加适当的索引。之后,将数据(批量)重新插入到新表中,并确保在批量中指定订单依据。
您可以使用这种方法来批量重新插入数据:
DECLARE @Count INT
SET @Count = 1
WHILE @Count > 0
BEGIN
INSERT INTO NewTable(Columns)
SELECT 10000 Columns
FROM
OriginalTable
ORDER BY ID
SET @Count = @@ROWCOUNT
END
它将在每次运行中插入10千行。您可以根据自己的喜好增加或减少行数。
现在,您可以在新表中测试查询,查看其性能。
您可能还需要根据WHERE子句为实际查询(在文章中)创建一个索引。
因此,您的WHERE子句将如下所示:
WHERE
lprArchived = 0
AND lprPendingDelete = 0
AND lprDone=0
AND lprOwner IN (.....)
您的索引将是:
CREATE INDEX [XReport] ON (lprArchived, lprPendingDelete, lprDone, lprOwner) INCLUDE (lprKey, lprRptName, lprTitle, lprDate, lprOwner, lprUserID, lprArchiveDate, lprTrackDate, lprActionView, lprActionEmail, lprActionExcel, lprActionForward, lprActionReassign, lprActionDownload, lprActionLocalPrint, lprActionServerPrint, lprPageCount, lprBytes, lprDataType, lprArchived, lprJobName, lprViewed, lprRptID)
针对lprOwner IN (.....)
的AS,您还可以采用另一种方法:
第一个(因为您有大量的值),我将考虑表中的lprOwner值与当前使用的值之间的比较。要查看表中实际剩余的数量,因为您有94个值。
基本上:
SELECT DISTINCT lprOwner FROM ReportIndex WHERE lprOwner NOT IN(current used values)
如果返回的总值少于94行,则使用反向方法会更好。会使用类似这样的东西:
AND lprOwner NOT IN(the returned values)
您还可以使用Temp或TVP方法将它们与查询结合在一起,这样便可以
SELECT ....
FROM ReportIndex ri
LEFT JOIN (The values table) t ON ri.lprOwner = t.lprOwner
WHERE
lprArchived = 0
AND lprPendingDelete = 0
AND lprDone=0
另一种方法是使用子查询 像这样:
SELECT *
FROM (
SELECT .... , lprOwner
FROM ReportIndex
WHERE
lprArchived = 0
AND lprPendingDelete = 0
AND lprDone=0
) D
WHERE
lprOwner .....
使用子查询方法,将首先通过lprArchived,lprPendingDelete和lprDone过滤结果。然后,将通过IprOwner过滤结果。因此,您要过滤两组而不是一组。有时候这在大表中可能很有用。
这就是我所要记住的,当然,并非所有方法或建议都可以在所有情况下都适用,但至少这个主意本身可以使您获得救赎