我想优化我的脚本,因为如果有数十万条记录,我会在获得结果时遇到响应缓慢。其目标如下:
这是我的表格结构,示例记录和我的查询:
CREATE TABLE Products
(
AltId VARCHAR(10),
ItemID VARCHAR(10),
ProductType BIT,
Buyer VARCHAR(6)
)
CREATE UNIQUE NONCLUSTERED INDEX idx_product
ON Products (AltId, ItemID)
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A01','ItemA0101',0,'216')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A01','ItemA0102',0,NULL)
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A01','ItemA0103',1,'264')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A01','ItemA0104',1,NULL)
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A02','ItemA0201',0,'215')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A02','ItemA0202',1,'217')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A03','ItemA0301',0,'215')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A03','ItemA0302',1,'216')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A03','ItemA0303',1,'264')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A04','ItemA0401',1,'216')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A05','ItemA0501',1,'218')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A05','ItemA0502',0,'216')
INSERT INTO Products
(Altid,ItemId,ProductType,Buyer)
VALUES ('A05','ItemA0503',1,NULL);
WITH original_query
AS (
--GET ALL ALTID FROM PRODUCTS THAT HAVE MORE THAN 1 BUYER
--JOIN MANUF TABLE TO GET ALL COLUMNS NEEDED FOR BUYERS
SELECT b.altid,
p.itemid,
p.Buyer,
p.ProductType--p.manufid
FROM (
--GET ALL ALTID THAT HAS MORE THAN 1 BUYER
SELECT a.altid
FROM (
--GET ALL ALT ID AND GROUP IT BY ALTID AND BUYER
SELECT p.altid
FROM products p
--WHERE p.BUYER IS NOT NULL
GROUP BY p.altid,
p.Buyer) a
GROUP BY a.altid
HAVING Count(*) > 1)b
JOIN products p
ON b.altid = p.altid
--JOIN manuf m ON p.manufid = m.manufid
--WHERE p.BUYER IS NOT NULL
), -- Get all Null value Buyer
GetAllNullBuyer
AS (SELECT oq.altid,
oq.itemid,
oq.Buyer,
oq.ProductType
FROM original_query oq
WHERE oq.Buyer IS NULL), --Get all buyer that has no null value
GetAllNotNullBuyer
AS (SELECT oq.altid,
oq.itemid,
oq.Buyer,
oq.ProductType
FROM original_query oq --JOIN Products p --Result
--ON oq.AltID=p.AltID
WHERE oq.Buyer IS NOT NULL
--Group by oq.altid,oq.Buyer,p.ProductType
), --Count the Buyer per altid and producttype
Count_AltidperBuyer
AS (SELECT a.altid,
a.Buyer,
Count(a.Buyer) BuyerCnt,
a.ProductType
FROM GetAllNotNullBuyer a
GROUP BY a.altid,
a.Buyer,
a.ProductType), --Get list of buyer with producttype=1 and not more than 1 buyer
exclude_rec
AS (SELECT altid,
Count(Buyer) BuyerCnt
FROM Count_AltidperBuyer
WHERE ProductType = 1
GROUP BY altid
HAVING Count(Buyer) > 1), --Combine all buyer with value and null value but did not inlcude buyer in the exclude_rec
CombineNullBuyer
AS (SELECT altid,
itemid,
Buyer,
producttype
FROM GetAllNotNullBuyer
WHERE altid NOT IN (SELECT altid
FROM exclude_rec)
UNION
SELECT altid,
itemid,
Buyer,
producttype
FROM GetAllNullBuyer
WHERE altid NOT IN (SELECT altid
FROM exclude_rec)), --GET all altid with producttype=1 and buyer is null
GetProductTypeBuyer
AS (SELECT a.altid,
a.itemid,
a.producttype,
a.Buyer
FROM CombineNullBuyer a
JOIN products p
ON a.altid = p.altid
AND a.itemid = p.itemid
WHERE p.producttype = 1
AND p.buyer IS NULL), --Combine records with producttype=1 and buyer is null and CombineNullBuyer records
CombineALL
AS (SELECT altid,
itemid,
Buyer,
producttype
FROM CombineNullBuyer
UNION
SELECT altid,
itemid,
Buyer,
producttype
FROM GetProductTypeBuyer), --Assign new Buyer ID
Assign_Buyer
AS (SELECT r.altid,
r.itemid,
r.Buyer,
r.producttype,
NewBuyer=Isnull((SELECT TOP 1 x.Buyer
FROM Products x
WHERE x.altid = r.altid
AND x.producttype = 1), r.Buyer)
FROM CombineALL r), --This will assign new buyer ID to buyer is null and producttype=1
RevisedBuyer
AS (SELECT *,
( Dense_rank()
OVER(
PARTITION BY altid
ORDER BY Buyer DESC) ) AS SeqNo
FROM Assign_Buyer)
SELECT p.AltID,
p.ItemID,
ab.Buyer,
ab.ProductType,
ab.NewBuyer,
( CASE
WHEN ab.NewBuyer IS NULL
AND ab.ProductType = 1 THEN (SELECT TOP 1 x.Buyer
FROM RevisedBuyer x
WHERE x.altid = ab.altid
AND x.producttype = 1
AND x.SeqNo = 1)
ELSE ab.NewBuyer
END ) AS Buyer1
FROM Products p
JOIN Assign_Buyer ab
ON p.AltID = ab.AltID
AND p.Itemid = ab.ItemID
WHERE Isnull(p.Buyer, '') <> Isnull(CASE
WHEN ab.NewBuyer IS NULL
AND ab.ProductType = 1 THEN (SELECT TOP 1 x.Buyer
FROM RevisedBuyer x
WHERE x.altid = ab.altid
AND x.producttype = 1
AND x.SeqNo = 1)
ELSE ab.NewBuyer
END, '')
ORDER BY ab.altid,
ab.itemid
答案 0 :(得分:0)
Here's my Solution to my problems:
--Save to temporary product table
SELECT b.altid, p.itemid, p.Buyer,p.ProductType INTO #products
FROM
(
--GET ALL ALTID THAT HAS MORE THAN 1 ALTID
SELECT a.altid FROM
(
--GET ALL ALT ID AND GROUP IT BY ALTID AND BUYER
SELECT p.altid FROM products p
--WHERE p.BUYER IS NOT NULL
GROUP BY p.altid,p.ProductType,p.Buyer
) a
GROUP BY a.altid
HAVING COUNT(*) > 1
)b
JOIN products p ON b.altid = p.altid
CREATE UNIQUE NONCLUSTERED INDEX idx_product on #products(altid,itemid)
--- End save temp table
---Temporary table for excluded record wherein it has more than 2 buyer with prodcuttype=1
;WITH Count_AltidperBuyer AS
(
SELECT altid,Buyer,count(Buyer) Cnt,ProductType
FROM #products
Group by altid,Buyer,ProductType
),--List of altid that has more than 2 manufid with producttype=1
exclude_rec AS(
Select altid,count(Buyer) BuyerCnt
from Count_AltidperBuyer
where ProductType=1
group by altid
having count(Buyer)>1
)
SELECT altid INTO #excluded_altid FROM exclude_rec
CREATE UNIQUE NONCLUSTERED INDEX idx_excluded_altid on #excluded_altid(altid)
---End of temporary excluded record
;WITH cte AS
(--Does not include records found in #excluded_altid table
SELECT altid,itemid,Buyer,producttype
FROM #products
WHERE altid NOT IN (SELECT altid FROM #excluded_altid )
), cte2 AS
(
SELECT a.altid,a.itemid,a.producttype,a.Buyer
FROM cte a JOIN products p
ON a.altid=p.altid AND a.itemid=p.itemid
WHERE p.producttype=1 and p.buyer is null
UNION
SELECT a.altid,a.itemid,a.producttype,a.Buyer
FROM cte a
),Assign_Buyer AS
(
SELECT r.altid,r.itemid,r.Buyer,r.producttype,NewBuyer=ISNULL(
isnull((SELECT Top 1 x.Buyer FROM Products x WHERE x.altid=r.altid and x.producttype=1),r.Buyer),
(SELECT Top 1 x.Buyer FROM Products x WHERE x.altid=r.altid and x.producttype=1 AND x.Buyer IS NOT NULL))
FROM cte2 r
)--,--This will assign new buyer ID to buyer is null and producttype=1
--Assign_BuyerToNewBuyer AS
--(
-- SELECT ab.altid,ab.itemid,ab.Buyer,ab.producttype,NewBuyer, NewBuyer2=(CASE WHEN NewBuyer IS NULL
-- THEN (SELECT Top 1 x.Buyer FROM Products x WHERE x.altid=ab.altid and x.producttype=1 AND x.Buyer IS NOT NULL)
-- ELSE NewBuyer END)
-- FROM Assign_Buyer ab
-- --SELECT *, (DENSE_RANK() OVER(PARTITION BY altid order by Buyer DESC)) AS SeqNo
-- --FROM Assign_Buyer
--)
SELECT p.AltID,p.ItemID,ab.Buyer,ab.ProductType,ab.NewBuyer
FROM Products p JOIN Assign_Buyer ab
ON p.AltID=ab.AltID AND p.Itemid=ab.ItemID
WHERE isnull(p.Buyer,'') <> isnull(ab.NewBuyer,'')
ORDER by ab.altid, ab.itemid
DROP TABLE #products
DROP TABLE #excluded_altid
SQL performance improves well compare to my old queries. What I did, I create a temporary table with index and then use WITH cte. If you have any other solution for this problems, please post your answer.
Thanks to @JamesZ for the comments and idea. Here's the link to SQL Fiddler for reference.