2017-07-26 67 views
2

我有一個表,這個結構獲得基於行順序

Create Table Example (
[order] INT, 
[typeID] INT 
) 

有了這些數據上的計數:

order|type 
1 7 
2 11 
3 11 
4 18 
5 5 
6 19 
7 5 
8 5 
9 3 
10 11 
11 11 
12 3 

我需要基於該順序來獲得各類型的數量,是這樣的:

type|count 
7  1 
11  **2** 
18  1 
5  1 
19  1 
5  **2** 
3  1 
11  **2** 
3  1 

語境

可以說這張桌子是關於房屋的,所以我列出了一個房屋訂單。所以,我有

  • 訂購1:紅房子
  • 2:白宮
  • 3:白宮
  • 4:紅房子
  • 5:藍色的房子
  • 6:藍色房子
  • 7:白宮

所以I N以顯示信息濃縮。我需要說:

  • 我有1紅房子
  • 然後,我有2間白色的房子
  • 然後,我有1間紅房子
  • 然後,我有2樓藍色的房子
  • 然後,我有1白宮

所以計數是基於訂單。如果我能夠在分區更改時重置RANK,DENSE_RANK函數將對我有所幫助。

+1

到目前爲止您嘗試過什麼?你的計數邏輯是什麼?如果它只是按類型,不應該類型5計數3而不是2? – Eric

+1

我相信你需要更詳細地解釋你的邏輯。 – ViKiNG

+1

我完全不理解你期望在這裏輸出什麼?你似乎對你想要的東西有非常深刻的理解,但這個解釋對我來說根本沒有任何意義。我正在考慮在刪除答案中發佈的scsimon。 –

回答

2

此解決方案使用遞歸CTE並依賴於無間隙order值。如果你沒有這個,你可以在飛行與ROW_NUMBER()創建:

DECLARE @mockup TABLE([order] INT,[type] INT); 
INSERT INTO @mockup VALUES 
(1,7) 
,(2,11) 
,(3,11) 
,(4,18) 
,(5,5) 
,(6,19) 
,(7,5) 
,(8,5) 
,(9,3) 
,(10,11) 
,(11,11) 
,(12,3); 

WITH recCTE AS 
(
    SELECT m.[order] 
      ,m.[type] 
      ,1 AS IncCounter 
      ,1 AS [Rank] 
    FROM @mockup AS m 
    WHERE m.[order]=1 

    UNION ALL 

    SELECT m.[order] 
      ,m.[type] 
      ,CASE WHEN m.[type]=r.[type] THEN r.IncCounter+1 ELSE 1 END 
      ,CASE WHEN m.[type]<>r.[type] THEN r.[Rank]+1 ELSE r.[Rank] END 
    FROM @mockup AS m 
    INNER JOIN recCTE AS r ON m.[order]=r.[order]+1 
) 
SELECT recCTE.[type] 
     ,MAX(recCTE.[IncCounter]) 
     ,recCTE.[Rank] 
FROM recCTE 
GROUP BY recCTE.[type], recCTE.[Rank]; 

遞歸遍歷是上下行增加計數器如果類型不變,提高排名。如果類型爲不同。

剩下的就是一個簡單的GROUP BY

+0

這就是我要找的,謝謝先生! – hardkoded

5

所以我有一個答案,但我必須警告你,它可能會得到一些提高的眉毛,因爲它是如何完成的。它使用一種被稱爲「Quirky Update」的東西。如果你打算實施這個,請爲上帝的愛閱讀鏈接文章,並理解這是一個「無證書黑客」,需要正確實施,以避免意想不到的後果。

如果你有一點點的數據,我只是爲了簡單和清晰起見,通過摺疊行來做它。但是,如果您有大量數據並仍需要高性能,則可能會有這種情況。

要求

  1. 表必須在順序要在
  2. 表進步必須有沒有其他的索引的聚簇索引(這可能導致SQL從另一個指標是讀取數據不正確的順序,導致行順序的量子疊加崩潰)。
  3. 表必須在操作過程中被完全鎖定(TABLOCKX)
  4. 更新必須以串行的方式進步(MAXDOP 1)

它做什麼

你怎麼知道的人告訴你,對錶中的數據沒有隱式的順序? 99%的時間仍然如此。除了我們知道最終它必須以某種順序存儲在磁盤上。這就是我們在這裏開發的訂單。通過強制進行聚簇索引更新以及可以在更新列的同一個更新語句中分配變量的事實,可以非常快速地有效滾動數據。

讓我們設置數據:

if object_id('tempdb.dbo.#t') is not null drop table #t 
create table #t 
(
    _order int primary key clustered, 
    _type int, 
    _grp int 
) 

insert into #t (_order, _type) 
select 1,7 
union all select 2,11 
union all select 3,11 
union all select 4,18 
union all select 5,5 
union all select 6,19 
union all select 7,5 
union all select 8,5 
union all select 9,3 
union all select 10,11 
union all select 11,11 
union all select 12,3 

這裏的更新語句。我會通過每個組件的下方走

declare @Order int, @Type int, @Grp int 

update #t with (tablockx) 
set @Order = _order, 
    @Grp = case when _order = 1 then 1 
       when _type != @Type then @grp + 1 
       else @Grp 
      end, 
    @Type = _type, 
    _grp = @Grp 
option (maxdop 1) 
  1. 更新與(tablockx)執行。如果你正在使用臨時表,你知道桌面上沒有爭用,但仍然是一個很好的習慣(如果使用這種方法甚至可以被認爲是一個很好的習慣)。
  2. Set @Order = _order。這看起來像一個毫無意義的陳述,而且它是。但由於_order是表的主鍵,因此將其分配給變量將強制S​​QL執行聚集索引更新,這對於此工作至關重要。
  3. 填充整數以表示所需的順序組。這就是魔術發生的地方,你必須從它在表格中滾動來考慮它。當_order爲1(第一行)時,只需將@Grp變量設置爲1.如果在任何給定行上,列值_type與變量值@type不同,我們會增加分組變量。如果這些值相同,我們只需堅持上一行的@Grp
  4. 更新@Type變量與_type列的值。請注意,在爲@Grp分配正確的值後,這將會發生。
  5. 最後,設置_grp = @Grp。這是實際列值用步驟3的結果更新的位置。
  6. 所有這些都必須使用option (maxdop 1)完成。這意味着最大並行度被設置爲1.換句話說,SQL不能執行任何可能導致排序關閉的任務並行化。

現在這只是一個由_grp字段分組的問題。對於每個連續批次_type,您將擁有唯一的_grp值。

結論

如果這似乎香蕉和哈克,它是。就像所有的事情一樣,你需要用一點鹽來解決這個問題,如果你打算實施這個概念,我會推薦真正玩弄這個概念,以便充分理解它,因爲我保證沒有其他人會知道如何排除故障如果你在半夜接到一個電話,說明它已經壞了。

+3

古怪的更新是我很熟悉的東西。通過發佈Jeff Moden文章的鏈接,你做得很好。 –

+0

哇,我尊敬的人,我會給你一個鏡頭,但你的回答肯定對社區有很大的幫助。 – hardkoded

+2

雖然我認爲,這是*用鏈鋸切割麪包*: - 答案很好,並且顯示出非常深刻的洞察力。 +1從我身邊! – Shnugo

2

我想我會發布的另一種方法我工作了,我想沿着dense_rank()工作其他人都在思考的線條更。這個假設唯一的事情是_order是一個順序整數(即沒有間隙)。

相同的數據設置爲前:

if object_id('tempdb.dbo.#t') is not null drop table #t 
create table #t 
(
    _order int primary key clustered, 
    _type int, 
    _grp int 
) 

insert into #t (_order, _type) 
select 1,7 
union all select 2,11 
union all select 3,11 
union all select 4,18 
union all select 5,5 
union all select 6,19 
union all select 7,5 
union all select 8,5 
union all select 9,3 
union all select 10,11 
union all select 11,11 
union all select 12,3 

什麼此方法確實是row_number每個_type,這樣無論在哪裏,一個_type存在,多少次,該類型將有順序的唯一ROW_NUMBER _order字段。通過從全局行號中減去該類型特定的行號(即_order),您將最終得到組。下面是這個代碼,然後我將介紹這一點。

;with tr as 
(
    select 
     -- Create an incrementing integer row_number over each _type (regardless of it's position in the sequence) 
     _type_rid = row_number() over (partition by _type order by _order), 
     -- This shows that on rows 6-8 (the transition between type 19 and 5), naively they're all assigned the same group 
     naive_type_rid = _order - row_number() over (partition by _type order by _order), 
     -- By adding a value to the type_rid which is a function of _type, those two values are distinct. 
     -- Originally I just added the value, but I think squaring it ensures that there can't ever be another gap of 1 
     true_type_rid = (_order - row_number() over (partition by _type order by _order)) + power(_type, 2), 
     _type, 
     _order 
    from #t 
    -- order by _order -- uncomment this if you want to run the inner select separately 
) 
select 
    _grp = dense_rank() over (order by max(_order)), 
    _type = max(_type) 
from tr 
group by true_type_rid 
order by max(_order) 

這是怎麼回事

首先第一件事情;我不必在src cte中創建單獨的列以返回_type_rid。我主要是爲了排除故障和清晰。其次,我也沒有必要在列_grp的最後選擇上做第二個dense_rank。我只是這樣做,所以它完全符合我另一種方法的結果。

在每種類型中,type_rid是唯一的,並且遞增1. _order也遞增1。所以只要一個給定的類型沿着,只差1個,_order - _type_rid將是相同的價值。讓我們看幾個例子(這是src CTE的結果,通過_order訂購):

_type_rid   naive_type_rid  true_type_rid  _type  _order 
-------------------- -------------------- -------------------- ----------- ----------- 
1     8     17     3   9 
2     10     19     3   12 
1     4     29     5   5 
2     5     30     5   7 
3     5     30     5   8 
1     0     49     7   1 
1     1     122     11   2 
2     1     122     11   3 
3     7     128     11   10 
4     7     128     11   11 
1     3     327     18   4 
1     5     366     19   6 

第一行,_order - _type_rid = 1 - 1 = 0。這此行(類型7)分配給組0 第二行,2 - 1 = 1。這將類型11分配給組1 第三行,3 - 2 = 1.這將第二個順序類型11分配給組1也 第四行,4 - 1 = 3。鍵入18到組3 ...等等。

這些組不是連續的,而是與_order的順序相同,這是重要的部分。您還會注意到我也將該值添加爲_type。這是因爲當我們碰到後面的一些行時,組切換,但是序列仍然增加1.通過添加_type,我們可以區分那些逐個值,並且仍然按照正確的順序進行。

最終外部從src訂單由最大(_order)(在我不必要的dense_rank() _grp修改,只是一般的結果順序)。

結論

這還是有點靠不住,但絕對內的「支持的功能」的界限很好。鑑於我在那裏遇到了一個問題(一件事情),可能還有其他我沒有考慮過的問題,所以再次嘗試一下,做一些測試。

+1

擊敗我一拳......我在流浪,如果我看到使用排名功能的基於集合的方法。做得好! –