Bir grup üzerinde sütun başına NULL olmayan son değerler kümesi nasıl seçilir?


9

SQL Server 2016 kullanıyorum ve tükettiğim veriler aşağıdaki forma sahip.

CREATE TABLE #tab (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));

INSERT INTO #tab VALUES 
    ('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
    ('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);

SELECT *
FROM    #tab;

resim açıklamasını buraya girin

Sütunlar üzerinde son boş olmayan değerleri elde etmek val1ve val2tarafından gruplandırılmış catve tarafından sipariş etmek istiyorum t. Aradığım sonuç

cat  val1 val2
A    1    P
B    10   C

Ben geldiğimde en son sipariş edilen null olmayan değere ihtiyacım olduğundan işe yaramayacakken LAST_VALUEgörmezden geliyor ORDER BY.

SELECT DISTINCT 
        cat, 
        LAST_VALUE(val1) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val1,
        LAST_VALUE(val2) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val2
FROM    #tab
cat  val1 val2
A    NULL NULL
B    10   NULL

Gerçek tabloda, catson boş olmayan değeri seçmek için daha fazla sütun (tarih ve dize sütunları) ve daha fazla val sütunu (tarih, dize ve sayı sütunları) bulunur.

Bu seçimin nasıl yapılacağı hakkında herhangi bir fikir.


1
@ Vérace Gruplayan: catby t.
Edmund

1
@ ypercubeᵀᴹ Hayır, eksik Q4 değeri yok, tdeğerler tekrarlanıyor. İyi davranılmış bir veri değildir.
Edmund

4
Tamam ama bu durumda, mükemmel bir sipariş belirleyen bir sipariş vermelisiniz. PARTITION BY cat ORDER BY t, idÖrneğin. Aksi takdirde, aynı sorgu (herhangi bir sorgu) ayrı yürütmelerde size farklı sonuçlar verebilir. Tablodaki sütunlar yalnızca gösterdiğiniz sütunlarsa, ancak nasıl belirli bir sıraya sahip olabileceğimizi görmüyorum!
ypercubeᵀᴹ

1
@ ypercubeᵀᴹ Sorun burada yatıyor. Veride kimlik sütunu yok. Birden çok gruplama sütunu, grup sıralaması içinde kullanılabilecek bir dize sütunu ve sonra nulls serpiştirilmiş birden çok değer sütunu vardır.
Edmund

1
SQL Server'a satırların hangi sırada olması gerektiğini belirleyemezseniz, bu verilerin herhangi bir tüketicisi farkı nasıl bilebilir?
Aaron Bertrand

Yanıtlar:


10

Itzik Ben Gan'ın NULL olmayan NULL Puzzle'daki birleştirme tekniğini kullanmak örnek tablonuz ve sütun veri türlerinizle böyle görünecektir.

select T.cat,
       cast(substring(
                     max(cast(T.t as binary(2)) + cast(T.val1 as binary(4))),
                     3,
                     4
                     ) as int),
       cast(substring(
                     max(cast(T.t as binary(2)) + cast(T.val2 as binary(1))),
                     3,
                     1
                     ) as char(1))
from #tab as T
group by T.cat;

resim açıklamasını buraya girin

Neler olup bittiğini daha iyi göstermek için adımları CTE'lere ayıran bu sorguyu yazmanın başka bir yolu. Yukarıdaki sorgu ile aynı yürütme planını verir.

with C1 as
(
  -- Concatenate the ordering column with the value column
  select T.cat,
        cast(T.t as binary(2)) + cast(T.val1 as binary(4)) as val1,
        cast(T.t as binary(2)) + cast(T.val2 as binary(1)) as val2
  from #tab as T
),
C2 as
(
  -- Get the max concatenated value per group
  select C1.cat,
         max(C1.val1) as val1,
         max(C1.val2) as val2
  from C1
  group by C1.cat
)
-- Extract the value from the concatenated column
select C2.cat,
       cast(substring(C2.val1, 3, 4) as int) as val1,
       cast(substring(C2.val2, 3, 1) as char(1)) as val2
from C2;

Bu çözüm, null değeri bir şeyle birleştirmenin null değerle sonuçlanması gerçeğini kullanır. CONCAT_NULL_YIELDS_NULL (Transact-sql) ayarlama


Çok iyi damıtılmış Mikael. Bu çözüm beni birkaç kez kurtardı, ancak Itzik'in makalesinin sonunu kafa karıştırıcı buldum. Gerçekte daha çok 1. adımın arkasındaki mantığı uygulamaya benzediğinde bunu "2. adım" olarak etiketledi.
pimbrouwers

2

Bölümde NULL için bir çek ekleyin

SELECT DISTINCT 
        cat, 
        FIRST_VALUE(val1) OVER(PARTITION BY cat ORDER BY CASE WHEN val1 is NULL then 0 else 1 END DESC, t desc) AS val1,
        FIRST_VALUE(val2) OVER(PARTITION BY cat ORDER BY CASE WHEN val2 is NULL then 0 else 1 END DESC, t desc) AS val2
FROM    #tab

0

Bunu yapmalı. row_number () ve birleştirme

Eğer iyi bir türünüz yoksa Q3'ten sadece birinin boş olmadığını ummanız gerekir.

declare @t TABLE (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));
INSERT INTO @t VALUES 
    ('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
    ('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);

--SELECT *
--     , row_number() over (partition by cat order by t) as rn
--FROM   @t
--where val1 is not null or val2 is not null;

select t1.cat, t1.val1, t2.val2 
from  ( SELECT t.cat, t.val1
             , row_number() over (partition by cat order by t desc) as rn
        FROM   @t t
        where val1 is not null 
       ) t1
join   ( SELECT t.cat, t.val2
             , row_number() over (partition by cat order by t desc) as rn
        FROM   @t t
        where val2 is not null 
       ) t2
   on t1.cat = t2.cat
  and t1.rn = 1
  and t2.rn = 1
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.