pilih baris yang terpenuhi kondisi untuk grup (tanpa tabel sementara)

10

Memiliki tabel dengan 3 kolom:

ID  category    flag
1       A       1
2       A       0
3       A       0
4       B       0
5       C       0

Saya ingin memilih semua baris yang memiliki flag = 1setidaknya satu kali per kategori.

Hasil yang diharapkan:

ID  category    flag
1       A       1
2       A       0
3       A       0

Itu bisa diselesaikan menggunakan tabel sementara seperti ini:

select ID into #tempTable from someTable where flag = 1
select * from someTable join #tempTable on someTable.ID = #tempTable.ID

Tetapi saya lebih suka solusi dengan pengelompokan, yang saya perjuangkan dengan susah payah. Bantuan apa pun akan dihargai.

Piotr Falkowski
sumber

Jawaban:

16

GROUP BYtidak dapat digunakan sendiri karena hanya mengembalikan 1 baris per grup ( category).


  • Anda dapat menggunakan sub kueri dengan flag = 1dan INNER JOIN:

    SELECT d1.ID, d1.category, d1.flag
    FROM data d1
    INNER JOIN (
        SELECT DISTINCT category FROM data WHERE flag = 1
    ) d2 
        ON d2.category = d1.category ;
    
  • Anda dapat menggunakan EXISTSklausa:

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE EXISTS (
        SELECT 1 FROM data WHERE flag = 1 AND category = d.category
    ) ;   
    
  • Anda dapat menggunakan INklausa (meskipun EXISTSlebih baik):

    SELECT d.ID, d.category, d.flag
    FROM data d
    WHERE d.category IN (SELECT category FROM data WHERE flag = 1) ;
    
  • Anda juga dapat menggunakan CROSS APPLYdengan sub kueri di flag = 1:

    SELECT d.ID, d.category, d.flag
    FROM data d
    CROSS APPLY (
        SELECT TOP (1) category 
        FROM data 
        WHERE flag = 1 AND category = d.category
    ) ca ;
    

DISTINCTtidak diperlukan jika, untuk setiap kategori, hanya 1 baris yang dapat dimiliki flag = 1.

Keluaran:

ID  category    flag
1       A       1
2       A       0
3       A       0
Julien Vavasseur
sumber
DISTINCT tidak perlu untuk predikat IN. Dan jika hanya satu baris per kategori dapat memiliki bendera 1, DISTINCT tidak perlu sama sekali.
Andriy M
@AndriyM benar tentang INkueri. Tetapi OP memiliki " Saya ingin memilih semua baris yang memiliki flag = 1 setidaknya sekali per kategori " yang membuat saya berpikir bahwa DISTINCTini diperlukan dalam pertanyaan lain.
ypercubeᵀᴹ
1
Dan dalam CROSS APPLY, SELECT DISTINCT categorymungkin harus lebih efisien jika diganti SELECT TOP (1) whatever. Ini akan secara efektif menjadi cara lain untuk menulis EXISTSsubquery.
ypercubeᵀᴹ
@Andriy Inilah sebabnya saya menambahkan catatan kemarin berdasarkan komentar awal Anda: tidak diperlukan jika hanya ada 1 baris dengan flag = 1.
Julien Vavasseur
4

Dengan asumsi itu Flagadalah BITkolom atau INTyang hanya mengambil 0dan 1sebagai nilai, ini dapat dicapai dengan menggunakan fungsi berjendela juga. Contohnya:

DECLARE @Test TABLE
(
  ID INT
  , Category VARCHAR(1)
  , Flag BIT
);

INSERT INTO @Test (ID, Category, Flag)
VALUES (1, 'A', 1)
  , (2, 'A', 0)
  , (3, 'A', 0)
  , (4, 'B', 0)
  , (5, 'C', 0);

SELECT T.ID
  , T.Category
  , T.Flag
FROM (
  SELECT ID
    , Category
    , Flag
    , MAX(CAST(Flag AS TINYINT)) OVER(PARTITION BY Category) AS MaxFlag
  FROM @Test
  ) AS T
WHERE T.MaxFlag = 1;

Itulah hasilnya:

ID Category Flag  
-- -------- ----- 
1  A        True  
2  A        False 
3  A        False 

Ini akan menemukan yang tertinggi Flaguntuk setiap kategori di tabel Anda, dalam kasus Anda itu mungkin benar / salah saja dan memilih satu yang true(1)hanya memiliki .

Konversi ke TINYINTdiperlukan karena MAXtidak menerima BITargumen.

Evaldas Buinauskas
sumber