DISTINCT FROM HERHANGİ BİRİ veya TÜMÜ ile birleştirilebilir mi?


13

Birleştirmenin bir postgres yolu mu IS DISTINCT FROMile ANYveya aynı sonucu almanın diğer bazı düzgün şekilde?

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <> any(array[null, 'A']);

 count
-------
     1
(1 row)

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo is distinct from any(array[null, 'A']);  

ERROR:  syntax error at or near "any"
LINE 3: where foo is distinct from any(array[null, 'A']);
                                   ^

Yanıtlar:


7

Belki böyle :

select foo
     , exists (values (null), ('A') except select foo) chk_any
     , not exists (values (null), ('A') intersect select foo) chk_all
from ( values ('A'),('Z'),(null) ) z(foo);

 foo | chk_any | chk_all
-----+---------+---------
 A   | t       | f
 Z   | t       | t
     | t       | f

Sadece null"dizi" değil, aynı zamanda nullin zde bu şekilde karşılaştırıldığını unutmayın.


13

Bir dilbilgisi sorunu olarak bakmak, ANY( Satır ve Dizi Karşılaştırmaları'nda ) olarak tanımlanır :

ifade operatörü ANY (dizi ifadesi)

Ama is distinct frombir operatör değil, Karşılaştırma Operatörleri'nde anlatıldığı gibi bir "yapı" :

Bu davranış uygun değilse, IS [NOT] DISTINCT FROM yapılarını kullanın

PostgreSQL'de kullanıcı tanımlı işleçler bulunduğundan, bu amaçla bir işleç / işlev birleşimi tanımlayabiliriz:

create function is_distinct_from(text, text) returns bool as 
'select $1 is distinct from $2;' language sql;

create operator <!> (
 procedure=is_distinct_from(text,text),
 leftarg=text, rightarg=text
);

Sonra bundan önce gelebilir ANY:

select count(*)
from (select 'A' foo union all select 'Z' union all select null) z
where foo <!> any(array[null, 'A']);  
 Miktar 
-------
     3
(1 satır)

1
Mükemmel, anlayışlı cevap.
Erwin Brandstetter

Bu, özellikle @ Erwin'in gelişimi ile önerdiğim geçici çözümden kesinlikle çok daha üstündür.
Andriy M

Bu cevap ve @ Erwin'in önerdiği ince ayarlar gerçekten mükemmel. Andriy'yi kabul ediyorum ama bu sadece kişisel bir tercih örneği: Eminim ki birçoğu sizinkinin şıklığını tercih edecektir.
Jack diyor ki topanswers.xyz

@JackDouglas: Standart operatörlerle alternatif bir çözüm ekledim.
Erwin Brandstetter

Bu talihsiz bir durum ... tüm niyet ve amaçlar IS DISTINCT FROMiçin operatör olmamalı mı? Anlamsal bir sorundan ziyade ayrıştırıcının teknik bir sınırlaması gibi görünüyor.
Andy

10

Şebeke

Bu, @ Daniel'in akıllı operatörü üzerine inşa edilmiştir .
Bu sırada, polimorfik tipler kullanarak fonksiyon / operatör birleşimini oluşturun . Sonra her tür için çalışır - tıpkı yapı gibi.
Ve işlevi yapın IMMUTABLE.

CREATE FUNCTION is_distinct_from(anyelement, anyelement)
  RETURNS bool LANGUAGE sql IMMUTABLE AS 
'SELECT $1 IS DISTINCT FROM $2';

CREATE OPERATOR <!> (
  PROCEDURE = is_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
);

Symbolhound ile hızlı bir arama bitti, bu nedenle operatör <!>modüllerin hiçbirinde kullanımda görünmüyor.

Eğer bu operatöre çok kullanacağız, sen (sorgu planlayıcısı yardımcı olmak biraz daha eti dışarı olabilir losthorse yorumunda önerilen gibi ). Yeni başlayanlar için, sorgu optimize edicisine yardımcı olmak üzere COMMUTATORve NEGATORcümlelerini ekleyebilirsiniz . CREATE OPERATORYukarıdakileri bununla değiştir :

CREATE OPERATOR <!> (
  PROCEDURE = is_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);

Ve Ekle:

CREATE FUNCTION is_not_distinct_from(anyelement, anyelement)
  RETURNS bool LANGUAGE sql IMMUTABLE AS 
'SELECT $1 IS NOT DISTINCT FROM $2';

CREATE OPERATOR =!= (
  PROCEDURE = is_not_distinct_from(anyelement,anyelement),
  LEFTARG  = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = =!=
, NEGATOR = <!>
);

Ancak ek hükümler eldeki kullanım durumuna yardımcı olmaz ve düz dizinler hala kullanılmaz. Bunu başarmak çok daha karmaşık. (Denemedim.) Ayrıntılar için kılavuzdaki "Operatör Optimizasyon Bilgileri" bölümünü okuyun .

Test durumu

Sorudaki test durumu ancak dizideki tüm değerler aynı olduğunda başarılı olabilir. Question ( '{null,A}'::text[]) içindeki dizi için sonuç her zaman TRUE olur. Bu amaçlanıyor mu? "TÜM FARKLIDIR" için başka bir test ekledim:

SELECT foo
     , foo <!> ANY ('{null,A}'::text[]) AS chk_any
     , foo <!> ALL ('{null,A}'::text[]) AS chk_all
FROM (
   VALUES ('A'),('Z'),(NULL)
   ) z(foo)

 foo | chk_any | chk_all
-----+---------+---------
 A   | t       | f
 Z   | t       | t
     | t       | f

Standart operatörlerle alternatif

foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax

olabilir neredeyse çevrilecek

foo = ALL (test_arr) IS NOT TRUE

foo = ALL (test_arr) verimler ...

TRUE .. eğer tüm elemanlar foo
FALSE.. herhangi bir NOT NULLeleman ise <> foo
NULL .. en az bir eleman IS NULLve hiçbir eleman<> foo

Yani, kalan köşe durumu
- foo IS NULL
- ve elemanlardan test_arrbaşka bir şeyden NULLoluşmuyor.

İkisinden biri hariç tutulabilirse, işimiz bitmiştir. Bu nedenle,
- sütunu tanımlanmışsa basit testi kullanın NOT NULL.
- ya sen biliyor dizisi tüm NULL asla.

Başka, ayrıca test edin:

AND ('A' = ALL(test_arr) IS NOT NULL OR 
     'B' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)

Burada 'A've 'B'olabilmektedir bir farklı değerleri. SO: Bu ilgili soru altında açıklama ve alternatifler
PostgreSQL tüm NULLs dizi

Yine, örneğin boş dizede var olmayan herhangi bir değeri biliyorsanız , yine de basitleştirebilirsiniz:test_arr''

AND ('' = ALL(test_arr) IS NOT NULL OR
     foo IS NOT NULL)

İşte tüm kombinasyonları kontrol etmek için eksiksiz bir test matrisi:

SELECT foo, test_arr
     , foo = ALL(test_arr) IS NOT TRUE  AS test_simple
     , foo = ALL(test_arr) IS NOT TRUE
       AND ('A' = ALL(test_arr) IS NOT NULL OR
            'B' = ALL(test_arr) IS NOT NULL OR 
            foo IS NOT NULL)            AS test_sure 
FROM (
   VALUES ('A'),('Z'),(NULL)
   ) v(foo)
CROSS JOIN (
   VALUES ('{null,A}'::text[]),('{A,A}'),('{null,null}')
   ) t(test_arr)

 foo |  test_arr   | test_simple | test_sure
-----+-------------+-------------+-----------
 A   | {NULL,A}    | t           | t
 A   | {A,A}       | f           | f   -- only TRUE case
 A   | {NULL,NULL} | t           | t
 Z   | {NULL,A}    | t           | t
 Z   | {A,A}       | t           | t
 Z   | {NULL,NULL} | t           | t
     | {NULL,A}    | t           | t
     | {A,A}       | t           | t
     | {NULL,NULL} | t           | f   -- special case

Bu, Andriy'nin EXCEPTçözümünden biraz daha ayrıntılı , ancak önemli ölçüde daha hızlı.


Oluştururken OPERATOR, COMMUTATOR(ve NEGATORbelki de ters IS NOT DISTINCT FROMoperatörle) yan tümcesi sağlanmalı mı? postgresql.org/docs/current/static/xoper-optimization.html
losthorse

1
@losthorse: Bunu ele alan bir parça ekledim.
Erwin Brandstetter

Bu gibi app_status (tamsayı) dayalı kayıtları ortadan kaldırmak için bu operatörü kullanıyorum app_status <!> any(array[3,6]). Ne yazık ki, kayıtlar üzerinde herhangi bir etkisi yoktur. Tamsayılarla çalışır mı?
M. Habib

@ M.Habib: Lütfen sorunuzu yeni bir soru olarak sorun . (İlgili tüm ayrıntılarla!) Bağlam için bunu her zaman bağlayabilir ve geri bağlantı oluşturmak için buraya bir yorum bırakabilirsiniz.
Erwin Brandstetter
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.