2016-05-05 8 views
1

ich auf meiner ersten Frage auf Stack bin, wie ich habe ein paar Tage, da ich in dieser Angelegenheit zu kämpfen:Wie man eine Abfrage auf mehreren Spalten macht, ohne alle möglichen Permutationen zu schreiben?

ich eine komplexe Abfrage (PLSQL) auf einem Tisch machen will, die col1, col2, col3 hat , col4, COL5 Werte wie (Namen: die Split- ein Teil pro Spalte) mit

+------+--------+--------+--------+------+ 
| ID | Col1 | Col2 | Col3 | Col4 | 
+------+--------+--------+--------+------+ 
| (#1) | Andrew | Joan | Bach | Mike | 
| (#2) | Mark | Andrew | Livy |  | 
| (#3) | Joan | Arch | Donnie |  | 
| (#4) | Joan | Andrew | Lyx |  | 
+------+--------+--------+--------+------+ 

Anzahl der Namensteile unterscheiden sich von 1 bis 5.

ich möchte in verschiedenen Kombinationen wie folgt suchen :

  • Suche nach Bach Joan Mike - get # 1, # 3, # 4 - in dieser Reihenfolge
  • Suche nach Andrew Bach - folgenden Reihenfolge # 1 erhalten, # 2, # 4 - in dieser Reihenfolge

ich mag nicht die Idee, eine looooong Abfrage verwenden, in denen ich alle möglichen Permutationen, um schreiben, wird jedes Teil meiner Suchbegriffs mach

Was Ich möchte, dies zu erreichen:

  • ersten gesetzt: Alle jene n Teile (beide Bach und Joan und Bach Spiel, in beliebiger Reihenfolge)
  • zweiten Satz: match n-1 Teile (mindestens N-1 meiner Suche Teile der Zeile, in beliebiger Reihenfolge)
  • dritten Satz: Spiel n-2 Teile

Ich verwende eine Datenbank ORACLE und ich dachte an sie in einer gespeicherten Prozedur zu machen: match_my_set (query_str, col1, col2, col3, col4, COL5). Ich würde mindestens 5 Loops (Loop in Loop) schreiben, um dies zu erreichen, aber ich bezweifle, dass es eine professionelle Idee ist. .

Jede Hilfe wird geschätzt. Danke

+0

Ihre Frage ist nicht klar ... was bedeuten 'erhalten # 1, # 3, # 4 bestellen' \t Bitte lesen Sie [** How-to-Ask **] (http://stackoverflow.com/help/how-to-ask) \t \t Und hier ist ein großartiger Ort, um [** START **] (http: // spaghettidba.com/2015/04/24/how-to-post-a-t-sql-Frage-auf-einem-öffentlichen-Forum /) zu lernen, wie Sie Ihre Fragequalität verbessern und bessere Antworten erhalten. –

+0

Nun, ich habe ein kleines Update hinzugefügt. Hoffe, es hilft – Gigg

+0

Ich mache ein Update mit, warum ich was Sie haben? Bitte überprüfen oder rückgängig machen, wenn nicht korrekt. Sie erwähnen Col5, aber Ihr Beispiel sieht nur aus wie 4 –

Antwort

5

Wenn Sie auf 11g oder höher sind, können Sie Ihre Spalten in Zeilen aufheben; Dies wird unter Verwendung eines CTE Ihre Beispieldaten zur Verfügung zu stellen:

with t (id, col1, col2, col3, col4, col5) as (
    select 1, 'Andrew', 'Joan', 'Bach', 'Mike', null from dual 
    union all select 2, 'Mark', 'Andrew', 'Livy', null, null from dual 
    union all select 3, 'Joan', 'Arch', 'Donnie', null, null from dual 
    union all select 4, 'Joan', 'Andrew', 'Lyx' , null, null from dual 
) 
select * from t 
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5)); 

     ID  COL_NO NAME 
---------- ---------- ------ 
     1   1 Andrew 
     1   2 Joan 
     1   3 Bach 
     1   4 Mike 
     2   1 Mark 
     2   2 Andrew 
     2   3 Livy 
... 

Dann können Sie für die Spiele gegen die einzelnen Namensspalte aussehen:

select distinct id 
from (
    select * from t 
    unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5)) 
) 
where name in ('Bach', 'Joan', 'Mike') 
order by id; 

     ID 
---------- 
     1 
     3 
     4 

ich denken, Sie wollen die Ordnung komplizierter obwohl machen , indem Sie zählen, wie viele der Begriffe in jeder Zeile übereinstimmen.Wenn ja, können Sie tun:

select id, count(*) as cnt 
from (
    select * from t 
    unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5)) 
) 
where name in ('Bach', 'Joan', 'Mike') 
group by id; 

     ID  CNT 
---------- ---------- 
     1   3 
     4   1 
     3   1 

und dann eine weitere Ebene der Inline-Ansicht müssen durch die Zählung zu bestellen, mit einer gewissen Art und Weise Verbindungen zu brechen:

select id 
from (
    select id, count(*) as cnt 
    from (
    select * from t 
    unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5)) 
) 
    where name in ('Bach', 'Joan', 'Mike') 
    group by id 
) 
order by cnt desc, id; 

, die mit Ihrer Beispieldaten das gleiche Ergebnis erhält . Das Ändern der IN Bedingung zu Benutzer ('Andrew', 'Bach') erhält auch 1,2,4 mit beiden Versionen.

Je nachdem, wie Sie die Werte erhalten, die Sie suchen, können Sie stattdessen ein Array (über einen Tabellensammlungsausdruck und einen Join) verwenden oder eine Zeichenfolge mit allen Suchbegriffen oder einigen Tokens in Tokens umwandeln andere Variante.

-1

Dies ist nicht getestet, aber ich denke, es wird funktionieren. Zunächst müssen Sie eine Funktion, die eine Suchzeichenfolge durch Leerzeichen in eine Tabelle aufgeteilt werden:

CREATE function [dbo].[SplitSpace] (@StringList varchar(4000)) 

RETURNS @Result Table(Value varchar(50)) 

AS 

BEGIN 

    DECLARE @x XML 
    SELECT @X = CAST('<A>' + REPLACE(@StringList, ' ', '</A><A>') + '</A>' AS XML) 

    INSERT INTO @Result 
    SELECT t.value('.', 'varchar(50)') as inVal 
    FROM @X.nodes('/A') AS x(t) 

    RETURN 
END 

Diese Funktion an anderer Stelle in handlichen kommt, auch, und man kann es leicht modifiziert durch Komma oder einen anderen Wert zu teilen, wenn benötigt.

Als nächstes müssen Sie eine Abfrage erstellen, die die gewünschten Ergebnisse liefert (wenn Sie mit dem Testen fertig sind, können Sie dies auf eine gespeicherte Prozedur konvertieren und die Suchzeichenfolge als Parameter akzeptieren):

DECLARE @SearchString varchar(255) = 'Bach Joan Mike' 
DECLARE @SearchTable TABLE(Value varchar(50)) 

INSERT INTO @SearchTable 
SELECT DISTINCT Value 
FROM SplitSpace(@SearchString) 

SELECT DISTINCT Col1, Col2, Col3, Col4 
FROM MyTable M 
JOIN @SearchTable S 
    ON S.Value = Col1 
    OR S.Value = Col2 
    OR S.Value = Col3 
    OR S.Value = Col4 
CROSS APPLY 
    (
     SELECT COUNT(*) AS [Number of Hits] 
     FROM @SearchTable 
     WHERE Value = M.Col1 
     OR Value = M.Col2 
     OR Value = M.Col3 
     OR Value = M.Col4 
    ) t 
ORDER BY t.[Number of Hits] DESC 

Im Grunde sagen Sie "geben Sie mir alle Datensätze, wo eine oder mehrere der" Name "Spalten in der Suchzeichenfolge vorhanden ist.

Dann sagen Sie, mit dem Cross Apply," für jede Zeile, erzählen wie viele Treffer ich habe. "Dann musst du nur noch nach der Anzahl der Treffer sortieren und schon bist du fertig.

Hinweis: Es ist möglich, dass jemand "Andrew Andrew Bach" als Suchbegriff eingeben kann. Wenn sie dies tun würden, würden Sie 2 Treffer für jede Spalte bekommen, in der Andrew drin ist und nur eine für jede, die Bach hat. Aus diesem Grund wählen Sie einen bestimmten Wert aus der von der Funktion zurückgegebenen Tabelle aus. Es beseitigt diese Duplikate. Wenn Sie mehrere Übereinstimmungen haben, erhalten Sie diesen Datensatz zweimal, da es sich um einen inneren Join handelt. Aus diesen Ergebnissen wählen Sie also col1, col2, col3, col4 aus, um auch diese Duplikate zu eliminieren.

Bitte lassen Sie mich wissen, wenn Sie irgendwelche Fragen haben.

+2

Das sieht als wäre es eine SQL Server-Lösung; Das OP befindet sich in einer Oracle-Datenbank. – Boneist

3

Sie können es tun Oracle die Sammlungen mit

Oracle-Setup (die in 10g oder später arbeiten sollten):

CREATE TABLE TABLE_NAME(ID, Col1, Col2, Col3, Col4) AS 
SELECT 1, 'Andrew', 'Joan', 'Bach', 'Mike' FROM DUAL UNION ALL 
SELECT 2, 'Mark', 'Andrew', 'Livy', NULL FROM DUAL UNION ALL 
SELECT 3, 'Joan', 'Arch', 'Donnie', NULL FROM DUAL UNION ALL 
SELECT 4, 'Joan', 'Andrew', 'Lyx', NULL FROM DUAL; 

CREATE TYPE stringlist AS TABLE OF VARCHAR2(100); 
/

Abfrage:

SELECT id, 
     col1, 
     col2, 
     col3, 
     col4 
FROM (
    SELECT t.*, 
     stringlist(col1, col2, col3, col4) 
      MULTISET INTERSECT 
      stringlist('Bach', 'Joan', 'Mike') -- Search terms 
      AS names 
    FROM TABLE_NAME t 
) 
WHERE names IS NOT EMPTY 
ORDER BY CARDINALITY(names) DESC, ID; 

Ausgabe :

 ID COL1 COL2 COL3 COL4 
---------- ------ ------ ------ ---- 
     1 Andrew Joan Bach Mike 
     3 Joan Arch Donnie  
     4 Joan Andrew Lyx   
Verwandte Themen