How To Avoid TempTable in Union All when queries contain DIFFERENT order by and inner join?

百般思念 提交于 2019-12-24 18:42:26

问题


What i am trying to do is always sending Product with 0 quantity to the end of an already sorted temp Table without losing current sorting (as i described in the following question How to send Zero Qty Products to the end of a PagedList<Products>?)

I have one Sorted temptable which is filled (it is sorted by what user has selected like Alphabetic , by Price or by Newer product,sorting is based identity id) :

CREATE TABLE #DisplayOrderTmp 
(
    [Id] int IDENTITY (1, 1) NOT NULL,
    [ProductId] int NOT NULL
)

sorted #DisplayOrderTmp :
+------------+---------------+  
|     id     |  ProductId    |  
+------------+---------------+  
|     1      |     66873     |  // Qty is 0
|     2      |     70735     |  // Qty is not 0
|     3      |     17121     |  // Qty is not 0
|     4      |     48512     |  // Qty is not 0
|     5      |     51213     |  // Qty is 0
+------------+---------------+

I want pass this data to web-page, but before it i need to send product with zero quantity to the end of this list without loosing current Sorting by)

My returned data should be like this (sorting doesn't changed just 0 quantity products went to the end of list by their order):

CREATE TABLE #DisplayOrderTmp4 
(
    [Id] int IDENTITY (1, 1) NOT NULL,
    [ProductId] int NOT NULL
)
+------------+---------------+  
|     id     |  ProductId    |  
+------------+---------------+  
|     1      |     70735     |  
|     2      |     17121     |  
|     3      |     48512     | 
|     4      |     66873     |   
|     5      |     51213     |  
+------------+---------------+ 

P.S: Its My product Table which i have to inner join with tmptable to find qty of products.

    Product Table is like this :
    +------------+---------------+------------------+  
    |     id     |   stockqty    | DisableBuyButton |
    +------------+---------------+------------------+  
    |   17121    |      1        |         0        | 
    |   48512    |      27       |         0        |     
    |   51213    |      0        |         1        |
    |   66873    |      0        |         1        |
    |   70735    |      11       |         0        |
    +------------+---------------+------------------+

What i have tried so far is this : (it works with delay and has performance issue i almost have 30k products)

INSERT INTO #DisplayOrderTmp2 ([ProductId])
        SELECT p2.ProductId
        FROM  #DisplayOrderTmp p2  with (NOLOCK) // it's already sorted table
        INNER JOIN Product prd with (NOLOCK) 
        ON p2.ProductId=prd.Id  
        and prd.DisableBuyButton=0     // to find product with qty more than 0
        group by p2.ProductId  order by min(p2.Id) // to save current ordering

INSERT INTO #DisplayOrderTmp3 ([ProductId])     
        SELECT p2.ProductId     
        FROM  #DisplayOrderTmp p2  with (NOLOCK) //it's already sorted table    
        INNER JOIN Product prd with (NOLOCK)
        ON p2.ProductId=prd.Id  
        and prd.DisableBuyButton=1    // to find product with qty equal to 0
        group by p2.ProductId  order by min(p2.Id) // to save current ordering


        INSERT INTO #DisplayOrderTmp4 ([ProductId]) // finally Union All this two data
          SELECT p2.ProductId FROM  
          #DisplayOrderTmp2 p2  with (NOLOCK)  // More than 0 qty products with saved ordering

        UNION ALL 

        SELECT p2.ProductId   FROM 
        #DisplayOrderTmp3 p2  with (NOLOCK)  //  0 qty products with saved ordering

Is there any way To Avoid creating TempTable in this query? send 0 quantity products of first temptable to the end of data-list without creating three other tempTable , without loosing current ordering based by Identity ID. My query has performance problem.

I have to say again that the temptable has a identity insert ID column and it is sorted based sorting type which user passed to Stored-Procedure. Thank You All :)


回答1:


Make sure the temp table has an index or primary key with Id as the leading column. This will help avoid sort operators in the plan for the ordering:

CREATE TABLE #DisplayOrderTmp
(
    [Id] int NOT NULL,
    [ProductId] int NOT NULL
    ,PRIMARY KEY CLUSTERED(Id)
);

With that index, you should be able to get the result without additional temp tables with reasonable efficiency using a UNION ALL query, assuming ProductID is the Product table primary key:

WITH products AS (
    SELECT p2.Id, p2.ProductId, prd.stockqty, 1 AS seq
    FROM #DisplayOrderTmp p2
    JOIN Product prd
          ON p2.ProductId=prd.Id
    WHERE prd.stockqty > 0
    UNION ALL
    SELECT p2.Id, p2.ProductId, prd.stockqty, 2 AS seq
    FROM #DisplayOrderTmp p2
    JOIN Product prd
          ON p2.ProductId=prd.Id
    WHERE prd.stockqty = 0
)
SELECT ProductId
FROM products
ORDER BY seq, Id;

You mentioned in comments that you ultimately want a paginated result. This can be done in T-SQL by adding OFFSET and FETCH to the ORDER BY clause as below. However, be aware that pagination over a large result set will become progressively slower the further into the result one queries.

WITH products AS (
    SELECT p2.Id, p2.ProductId, prd.stockqty, 1 AS seq
    FROM #DisplayOrderTmp p2
    JOIN Product prd
          ON p2.ProductId=prd.Id
    WHERE prd.stockqty > 0
    UNION ALL
    SELECT p2.Id, p2.ProductId, prd.stockqty, 2 AS seq
    FROM #DisplayOrderTmp p2
    JOIN Product prd
          ON p2.ProductId=prd.Id
    WHERE prd.stockqty = 0
)
SELECT ProductId
FROM products
ORDER BY seq, Id
OFFSET @PageSize * (@PageNumber - 1) ROWS
FETCH NEXT @PageSize ROWS ONLY;



回答2:


You could use ORDER BY without using UNION ALL:

SELECT p2.ProductId
FROM #DisplayOrderTmp p2
JOIN Product prd
  ON p2.ProductId=prd.Id  
ORDER BY prd.DisableBuyButton, p2.id;

DisableBuyButton = 0 - qnt > 0 DisableBuyButton = 1 - qnt = 0




回答3:


Seems it only needs an extra something in the order by.

An IIF or CASE can be used to give a priority to the sorting.

SELECT tmp.ProductId
FROM #DisplayOrderTmp tmp
JOIN Product prd
  ON prd.Id = tmp.ProductId
 AND prd.DisableBuyButton IN (0,1)
ORDER BY IIF(prd.DisableBuyButton=0,1,2), tmp.id;


来源:https://stackoverflow.com/questions/59439547/how-to-avoid-temptable-in-union-all-when-queries-contain-different-order-by-and

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!