简体   繁体   English

如何优化我的查询速度(避免对每一行使用子选择)?

[英]How to optimize my query speed (avoid using subselect for every row)?

I have a table called CisLinkLoadedData.我有一个名为 CisLinkLoadedData 的表。 Is has Distributor , Network , Product , DocumentDate , Weight , AmountCP and Quantity columns. Is 有DistributorNetworkProductDocumentDateWeightAmountCPQuantity列。 It used to store some product daily sales.它用于存储一些产品的日常销售。 AmountCP / Quantity is the price for the product at certain date. AmountCP / Quantity是产品在特定日期的价格 There are promo and regular sales, but no flag for it.促销定期销售,但没有标志。 We can tell if certain record is regular or promo by comparing it's price with the maximum recorded price within month.我们可以通过比较它的价格与一个月内的最高记录价格来判断某个记录是常规的还是促销的。 I did explained it on this picture.我确实在这张照片上解释过。 在此处输入图像描述

I need to make a query to display summarized regular and promo sales of certain product per month.我需要进行查询以显示每月某些产品的汇总常规和促销销售。 Well, I made it, but it very slow (6 minutes to execute at 1.6 millions records).好吧,我做到了,但它非常慢(6 分钟执行 160 万条记录)。 I suspect this is because I use subquery to determine max price for every record, but I don't know how to make it another way.我怀疑这是因为我使用子查询来确定每条记录的最高价格,但我不知道如何以另一种方式进行。

This is what I made:这是我做的:

SELECT
    Distributor,
    Network,
    Product,
    cast(month(DocumentDate) as VARCHAR) + '.' + cast(year(DocumentDate) as VARCHAR) AS MonthYear,
    SUM(Weight) AS MonthlyWeight,
    IsPromo
FROM (SELECT
        main_clld.Distributor,
        main_clld.Network,
        main_clld.Product,
        main_clld.DocumentDate,
        main_clld.Weight,
        main_clld.Quantity,
        main_clld.AmountCP,
        CASE WHEN (main_clld.AmountCP / main_clld.Quantity) < (SELECT MAX(sub_clld.AmountCP / NULLIF(sub_clld.Quantity, 0)) FROM CisLinkLoadedData AS sub_clld WHERE sub_clld.Distributor = main_clld.Distributor AND sub_clld.Network = main_clld.Network AND sub_clld.Product = main_clld.Product AND cast(month(sub_clld.DocumentDate) as VARCHAR) + '.' + cast(year(sub_clld.DocumentDate) as VARCHAR) = cast(month(main_clld.DocumentDate) as VARCHAR) + '.' + cast(year(main_clld.DocumentDate) as VARCHAR) AND sub_clld.Quantity > 0 AND sub_clld.GCRecord IS NULL) THEN 1 ELSE 0 END AS IsPromo
    FROM CisLinkLoadedData AS main_clld
    WHERE main_clld.Quantity > 0 AND main_clld.GCRecord IS NULL) AS bad_query
GROUP BY
    Distributor,
    Network,
    Product,
    cast(month(DocumentDate) as VARCHAR) + '.' + cast(year(DocumentDate) as VARCHAR),
    IsPromo;

What is possible to do in such case?在这种情况下可以做什么? By the way, if you can do result table with another structure like that ( Distributor , Network , Product , MonthYear , RegularWeight , PromoWeight ) - it's even better.顺便说一句,如果您可以使用类似的另一种结构( DistributorNetworkProductMonthYearRegularWeightPromoWeight )来制作结果表,那就更好了。 This is what I tried initially, but failed.这是我最初尝试过的,但失败了。 I use Microsoft SQL Server.我使用 Microsoft SQL Server。

Rather than a correlated subquery, you could use a windowed function to retrieve the maximum price per group (each group is defined by the partition by clause):您可以使用窗口函数而不是相关子查询来检索每组的最高价格(每个组由 partition by 子句定义):

MAX(main_clld.AmountCP / NULLIF(main_clld.Quantity, 0)) 
 OVER(PARTITION BY main_clld.Distributor, main_clld.Network, 
                   main_clld.Product, EOMONTH(main_clld.DocumentDate))

I think your full query would end up something like:我认为您的完整查询最终会是:

SELECT
    Distributor,
    Network,
    Product,
    MonthYear,
    SUM(Weight) AS MonthlyWeight,
    IsPromo
FROM (SELECT
        main_clld.Distributor,
        main_clld.Network,
        main_clld.Product,
        main_clld.DocumentDate,
        main_clld.Weight,
        main_clld.Quantity,
        main_clld.AmountCP,
        CAST(MONTH(DocumentDate) AS VARCHAR(2)) + '.' + cast(year(DocumentDate) as VARCHAR(2)) AS MonthYear,
        CASE WHEN (main_clld.AmountCP / main_clld.Quantity) < MAX(main_clld.AmountCP / NULLIF(main_clld.Quantity, 0)) 
                                                                OVER(PARTITION BY main_clld.Distributor, main_clld.Network,
                                                                                    main_clld.Product, EOMONTH(main_clld.DocumentDate)) 
                THEN 1 ELSE 0 END AS IsPromo
    FROM CisLinkLoadedData AS main_clld
    WHERE main_clld.Quantity > 0 
    AND main_clld.GCRecord IS NULL
    ) AS bad_query
GROUP BY
    Distributor,
    Network,
    Product,
    MonthYear,
    IsPromo;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM