2009-11-18 11 views
18

Aus Gründen, die ich nicht kontrollieren kann, muss ich zwei Tabellen verbinden und ich brauche NULL-Werte zu entsprechen. Die beste Option, die ich denken konnte, war eine UUID auszuspucken und dass mein Vergleichswert verwenden, aber es scheint hässlichSQL "Join" auf Null-Werte

SELECT * FROM T1 JOIN T2 ON nvl(T1.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') = 
    nvl(T2.SOMECOL,'f44087d5935dccbda23f71f3e9beb491') 

Wie kann ich besser machen? Dies ist auf Oracle, wenn es darauf ankommt, und der Kontext ist eine Anwendung, in der ein Stapel von vom Benutzer hochgeladenen Daten mit einem Stapel vorhandener Daten verglichen werden muss, um zu sehen, ob irgendwelche Zeilen übereinstimmen. Im Rückblick hätten wir verhindern sollen, dass irgendeine der Join-Spalten in beiden Datensätzen Nullen enthält, aber wir haben es nicht getan, und jetzt müssen wir damit leben.

Edit: Um klar zu sein, bin ich nicht nur besorgt mit Nullen. Wenn die Spalten nicht null sind, möchte ich, dass sie ihren tatsächlichen Werten entsprechen.

+2

Sind die Tabellen mit den Nullwerten verknüpft? Wenn das der Fall ist, werden Sie eine ziemlich große Ergebnismenge erhalten. Wenn zum Beispiel in T1 10 Nullen und in T2 10 Nullen vorhanden sind, erhalten Sie 100 Zeilen. Sicherlich können Sie auf einem anderen besiedelten Gebiet beizutreten, und wählen Sie nur ... Wo T1.SomeCol ist null und T2.SomeCol ist null –

+0

Anwenderfunktionen einen dramatischen Einfluss auf die Leistung haben, wenn sie in großen Abfragen verwendet, vielleicht könnten Sie verbessern Sie das, indem Sie persistent berechnete Felder verwenden. Ich bin mir nicht sicher, ich denke nur nach. –

+0

Was ist los mit Ihrer Lösung? Anstelle von UUID könnten Sie eine beliebige Zeichenfolge verwenden, die nicht in den Spalten enthalten ist, wie z. B. die Zeichenfolge "null", um den Speicherbedarf zu verringern. –

Antwort

33

Vielleicht würde dies funktionieren, aber ich habe nie versucht es tatsächlich:

SELECT * 
FROM T1 JOIN T2 
ON T1.SOMECOL = T2.SOMECOL OR (T1.SOMECOL IS NULL AND T2.SOMECOL IS NULL) 
+1

Ich habe das auch nicht versucht, aber mein Gehirn besteht darauf, dass Sie ein CROSS JOIN anstelle eines JOIN benötigen würden? Ich würde es nicht glauben, obwohl es den ganzen Tag hässlichen Java-Code durchlesen musste und ausgebrannt ist. – Powerlord

+1

@R. Bemrose - Sollte nur ein Cross-Join-Typ-Problem haben, wenn Sie mehrere Nullen haben. Wenn es nur eine Null gibt, sollte es in Ordnung funktionieren. Wenn Sie jedoch mehrere Nullen haben, gibt es wirklich keine Lösung, da es unmöglich wäre, zu entscheiden, welche Null mit welcher anderen Null übereinstimmen soll. –

+1

Ich war überrascht, aber das hat tatsächlich funktioniert. – Dan

5

In SQL Server ich verwendet habe:

WHERE (a.col = b.col OR COALESCE(a.col, b.col) IS NULL) 

Offensichtlich nicht effizient, weil der OR, aber es sei denn, es gibt einen reservierten Wert, den Sie NULL auf beiden Seiten ohne Mehrdeutigkeit oder Falten zuordnen können, das ist das Beste, was Sie tun können (und wenn es war, warum NULL sogar in Ihrem Design erlaubt war)

+0

Das ist gut, aber isnull ist schneller, wenn nur zwei Werte zu vergleichen. – JSideris

+0

Oh, aber ich denke, das ist nur in tsql ... – JSideris

3

Sie können nicht tun irgendein besser, aber der JOIN, den Sie haben, wird in keiner Weise ein tatsächliches "JOIN" machen (es gibt keine Korrelation zwischen T1.SOMECOL und T2.SOMECOL, außer dass beide einen NULL-Wert für diese Spalte haben). Das bedeutet im Wesentlichen, dass Sie keinen JOIN auf NULLs verwenden können, um zu sehen, ob die Zeilen übereinstimmen.

NULL ist nie gleich einem anderen NULL. Wie kann etwas von unbekanntem Wert einem anderen unbekannten Wert gleichkommen?

+0

Ich weiß konzeptionell kann es nicht. Diese spezielle Eigenschaft der Anwendung wurde dumm entworfen. Grundsätzlich müssen wir den Wert dieser Spalte im Datensatz eindeutig angeben. Dazu gehört, dass eine Zeile (aber nur eine) mit einem Nullwert vorhanden sein muss. Wenn die neuen Daten eintreffen und für diese Spalte ebenfalls einen Nullwert haben, möchten wir, dass sie übereinstimmen. – Dan

+0

Nicht konzeptionell. In Wirklichkeit. Wenn die vorhandenen Daten eine Schlüsselspalte haben, die null ist, und die neuen Daten eine Schlüsselspalte haben, die null ist, können sie + oder nicht + übereinstimmen. IOW, wenn eine vorhandene Zeile mit einem NULL-Schlüsselwert und 100 neue Zeilen mit jeweils einem NULL-Schlüsselwert vorhanden sind, der mit der vorhandenen Zeile übereinstimmt? Es gibt keinen "eindeutigen NULL-Wert" in dem Sinne, dass Ihre Daten versuchen, ihn zu verwenden. Oder, genauer, * alle * NULLs sind einzigartig; Faktisch stimmt niemand NULL mit einem anderen überein. –

0

Wirf es einfach da raus - gibt es eine Möglichkeit, diese Nullen zu einem bekannten Wert zu verschmelzen, wie eine leere Saite? Wenn Sie nicht viel darüber wissen, wie Ihr Tisch aufgebaut ist, kann ich nicht sicher sein, ob Sie auf diese Weise den Sinn verlieren - dh eine leere Zeichenfolge bedeutet "Benutzer weigerte sich, eine Telefonnummer einzugeben" und NULL war "wir haben vergessen danach fragen "oder so etwas?

Quoten sind es nicht möglich, ich bin sicher, aber wenn es ist, haben Sie bekannte Werte zu vergleichen, und Sie können eine legit Verbindung auf diese Weise bekommen.

0

Ist es nicht das gleiche wie das Vorhandensein von Nullen in beiden Spalten?

SELECT * FROM T1, T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL 

oder

SELECT * FROM T1 CROSS JOIN T2 WHERE T1.SOMECOL IS NULL and T2.SOMECOL IS NULL 
+1

sql vergleicht NULL mit dem Schlüsselwort "IS", nicht "==". Tatsächlich verwendet es '==' nirgendwo, einfach '='. –

+0

Sie haben Recht. Bearbeitet. Aber der Punkt steht immer noch. Ist das, was das OP gefragt hat, nicht effektiv in beiden Spalten nach Nullwerten zu suchen? Wenn Sie den anderen Werten ebenfalls beitreten müssen, können Sie Folgendes tun (T1.SOMECOL IST NULL UND T2.SOMECOL IST NULL) ODER (T1.SOMECOL = T2.SOMECOL). – Gambler

+0

Ich finde keinen Grund für diese Antwort -1 nach der Bearbeitung zu haben, also +1. –

1

Möchten Sie die Tabellen beitreten wollen in der Lage sein, um wirklich, wenn ein Wert Null ist? Können Sie nicht einfach die möglichen Nullwerte im Join-Prädikat ausschließen? Ich finde es schwer zu verstehen, dass Zeilen in zwei Tabellen mit einem Nullwert in Beziehung stehen können. Wenn Sie in Tabelle1.col_a 100 Nullen und in Tabelle2.col_b 100 Nullen haben, werden 10000 Zeilen nur für die Zeilen mit Null zurückgegeben. Es klingt falsch.

Allerdings haben Sie gesagt, dass Sie es brauchen. Kann ich vorschlagen, die Nullspalte zu einer kleineren Zeichenfolge zusammenzufassen, da Zeichenvergleiche relativ teuer sind. Noch besser: Verschmelzen Sie die Nullen zu einer Ganzzahl, wenn die Daten in den Spalten Text sind. Dann haben Sie sehr schnelle "Vergleiche" und es ist unwahrscheinlich, dass Sie mit vorhandenen Daten kollidieren.

+0

Ich komme zu spät zu dieser Party, aber ich habe einen guten Grund gefunden: 'user_segments' zu' user_objects' zu verbinden. Wenn die Tabelle partitioniert ist, müssen Sie auch 'Partitionsname' mit' Unterobjektname' verbinden. In nicht partitionierten Tabellen sind diese Werte jedoch null. –

3

für diese Art von Aufgabe Oracle verwendet intern eine nicht dokumentierte Funktion sys_op_map_nonnull(), wo Ihre Abfrage werden würde:

SELECT * 
FROM T1 JOIN T2 ON sys_op_map_nonnull(T1.SOMECOL) = sys_op_map_nonnull(T2.SOMECOL) 

Undocumented, also seien Sie vorsichtig, wenn Sie diesen Weg gehen.

3

Einfach, nutzen COALESCE, die ihre ersten Nicht-Null-Parameter zurück:

SELECT * FROM T1 JOIN T2 ON 
    COALESCE(T1.Field, 'magic string') = 
    COALESCE(T2.Field, 'magic string') 

Das einzige, was Sie befürchten müssen, ist, dass ‚Magic String‘ nicht unter den zulässigen Werten für die Verbindung sein kann Feld in jeder Tabelle.

+0

Was ist der Unterschied zwischen 'COALESCE' und' NVL' in diesem Fall? – Dan

+0

Ich entschuldige mich - ich habe eine MySQL-Frage beantwortet und sie wahrscheinlich nach der Suche an den falschen Ort gestellt, wenn sie vorher beantwortet wurde. Ihre Frage ist offensichtlich über ein Jahr alt und beantwortet. Ich hätte auch mehr Aufmerksamkeit darauf richten sollen, denn ein Profil der Abfrage in MySQL zeigt, dass "COALESCE" die Verwendung eines Indexes auf "Field" verhindert - die Antwort, die Sie als richtig markiert haben, hat diesen Nebeneffekt nicht. Allerdings kein großes Problem, da der Join in diesem speziellen Beispiel beide Tabellen abtastet –

-2

Sie können auch CASE verwenden den Nullwert in Subqueries zu ersetzen, dann die Ergebnisse JOIN:

SELECT T1.COL1 FROM 
(
    (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE1) T1 
    JOIN 
    (SELECT (CASE WHEN COL1 IS NULL THEN 'X' ELSE COL1 END) AS COL1 FROM TABLE2) T2 
) 
ON T1.COL1=T2.COL1 
+0

Das unterscheidet sich nicht konzeptionell von dem, was das OP gab, und es ist tatsächlich schlimmer, da X eher in der Datenbank ist. Auch die Inline-Ansichten sind unnötig und der JOIN ist falsch, da Sie ein kartesisches Produkt erstellt haben. –

0

Warum nicht so etwas wie das:

SELECT * FROM T1 T2 ON NVL (T1 JOIN. SOMECOL, 'null') = nvl (T2.SOMECOL, 'null')

Ich weiß nicht, warum Sie die UUID verwenden. Sie können eine beliebige Zeichenfolge verwenden, die nicht in den Spalten enthalten ist, z. B. die Zeichenfolge "null", um den Speicherbedarf zu verringern. Und die Lösung, die nvl verwendet, ist viel schneller als die Lösung, die zum Beispiel von Eric Petroelje or ... is null vorgeschlagen wird.

0

Sie könnten versuchen, mit der folgenden Abfrage verwenden.

SELECT * 
FROM TABLEA TA 
JOIN TABLEB TB ON NVL(TA.COL1,0)=NVL(TB.COL2,0); 
0

Ich glaube, Sie könnten noch nvl nutzen könnten() für beitreten:

SELECT * 
FROM T1 
JOIN T2 ON NVL(T2.COL1,-1)=NVL(T1.COL1,-1); 

Aber Sie müssen Funktion basierte Indizes hinzufügen auf Spalten col1

CREATE INDEX IND_1 ON T1 (NVL(COL1,-1)); 
CREATE INDEX IND_2 ON T2 (NVL(COL1,-1)); 

Indizes die Verbesserung sollte Geschwindigkeit der Verbindung auf NVL (..) erheblich.