2016-08-05 27 views
2

ACT und CAT sind Anagrammeprüfen Anagramme SQL Server

ich schreiben habe eine Funktion in sql server verwenden, die eine Boolesche Ausgabe 2 Strings und gegeben nimmt, dass die beiden von ihnen sind Anagramm oder nicht angibt, ob.

Diese sieht nicht sinnvoll, es in SQL Server zu tun, aber es ist für

+4

Diese Seite ist nicht entworfen, um Ihre Hausaufgaben nutzen sein. Bitte posten Sie zumindest einen Versuch, den Sie bereits selbst gemacht haben. –

+0

Die beiden Strings sollten für einen Start die gleiche Länge haben. Wenn dies der Fall ist, müssen Sie die Strings in einzelne Buchstaben aufteilen. Wahrscheinlich am besten, die Buchstaben in Tabellen zu legen. Dann müssen Sie überprüfen, dass sie die gleichen Buchstaben und die gleiche Anzahl pro Buchstabe haben. – HoneyBadger

Antwort

4

Erste split (T-SQL Split Word into characters) beiden Worte in temporäre Tabellen nur den Zweck zu lernen. Führen Sie dann einen äußeren Join aus und prüfen Sie, ob Nullwerte vorhanden sind.

bearbeiten dank George Kommentar:

  1. split (T-SQL Split Word into characters) beiden Worte in temporäre Tabellen
  2. temporäre Tabellen ändern oder CTEs verwenden Klausel eine Spalte mit count (*) mit einer Gruppe von Buchstaben
  3. hinzufügen
  4. Führen Sie einen vollständigen äußeren Join auf zwei temporären Tabellen mit einem Buchstaben und seine Anzahl in Join-Bedingung
  5. Überprüfen Sie auf Null in der Ausgabe - wenn es keine gibt, haben Sie ein Anagramm
+0

Vorsicht mit doppelten Zeichen. Zum Beispiel könnten die Wörter "ABCC" und "ABBC" als Anagramm erscheinen, während sie nicht sind. –

+1

Wahr.Eine andere Spalte, in der die Anzahl der einzelnen Buchstaben in beiden Tabellen gespeichert wird, und die Join-Bedingung sollten dies beheben. – PacoDePaco

7

SQL Server ist bei dieser Art von Dingen nicht gut, aber Sie sind hier:

WITH Src AS 
(
    SELECT * FROM (VALUES 
    ('CAT', 'ACT'), 
    ('CAR', 'RAC'), 
    ('BUZ', 'BUS'), 
    ('FUZZY', 'MUZZY'), 
    ('PACK', 'PACKS'), 
    ('AA', 'AA'), 
    ('ABCDEFG', 'GFEDCBA')) T(W1, W2) 
), Numbered AS 
(
    SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) Num 
    FROM Src 
), Splitted AS 
(
    SELECT Num, W1 Word1, W2 Word2, LEFT(W1, 1) L1, LEFT(W2, 1) L2, SUBSTRING(W1, 2, LEN(W1)) W1, SUBSTRING(W2, 2, LEN(W2)) W2 
    FROM Numbered 
    UNION ALL 
    SELECT Num, Word1, Word2, LEFT(W1, 1) L1, LEFT(W2, 1) L2, SUBSTRING(W1, 2, LEN(W1)) W1, SUBSTRING(W2, 2, LEN(W2)) W2 
    FROM Splitted 
    WHERE LEN(W1)>0 AND LEN(W2)>0 
), SplitOrdered AS 
(
    SELECT *, 
     ROW_NUMBER() OVER (PARTITION BY Num ORDER BY L1) LNum1, 
     ROW_NUMBER() OVER (PARTITION BY Num ORDER BY L2) LNum2 
    FROM Splitted 
) 
SELECT S1.Num, S1.Word1, S1.Word2, CASE WHEN COUNT(*)=LEN(S1.Word1) AND COUNT(*)=LEN(S1.Word2) THEN 1 ELSE 0 END Test 
FROM SplitOrdered S1 
JOIN SplitOrdered S2 ON S1.L1=S2.L2 AND S1.Num=S2.Num AND S1.LNum1=S2.LNum2 
GROUP BY S1.Num, S1.Word1, S1.Word2 

und Ergebnisse:

1 CAT  ACT  1 
2 CAR  RAC  1 
3 BUZ  BUS  0 
4 FUZZY MUZZY 0 
5 PACK PACKS 0 
6 AA  AA  1 
7 ABCDEFG GFEDCBA 1 
+0

Wow, sehr nette Abfrage! Versuchte es, funktioniert super, auch mit doppelten Zeichen (siehe meinen anderen Kommentar). BTW, lernte auch etwas neues, wusste nicht über die 'values'-Klausel in' select'. –

+1

@Tanner Ja, aber dies ist auch eine Frage-und-Antwort-Website, die anderen Benutzern Vorteile bietet. Aus dieser Sicht ist dies eine vollkommen fertige Abfrage, die von copy-paste verwendet werden kann, und Sie können auch davon profitieren, indem Sie sie analysieren, und daher sollte sie ** upvoted sein! ** –

+0

Es tut mir leid, aber während dies ein ist coole Verwendung von CTE diese Abfrage läuft eher langsam (auf meinem Server zumindest). 160 000 Zeilen in 24 Sek .. Ich habe eine Funktion mit einer Schleife erstellt, und sie dosiert den Job in 0. eine Sekunde. Vielleicht kann jemand anderes das auch testen/bestätigen? –

2

Die erste get in meinem Kopf:

DECLARE @word1 nvarchar(max) = NULL, 
     @word2 nvarchar(max) = 'Test 1', 
     @i int = 0, @n int 

DECLARE @table TABLE (
    id int, 
    letter int 
) 

SELECT @word1 = ISNULL(LOWER(@word1),''), @word2 = ISNULL(LOWER(@word2),'') 

SELECT @n = CASE WHEN LEN(@word1) > LEN(@word2) THEN LEN(@word1) ELSE LEN(@word2) END 

WHILE @n > 0 
BEGIN 
    INSERT INTO @table 
    SELECT 1, ASCII(SUBSTRING(@word1,@n,1)) 
    UNION ALL 
    SELECT 2, ASCII(SUBSTRING(@word2,@n,1)) 
    SET @[email protected] 
END 

SELECT CASE WHEN COUNT(*) = 0 THEN 1 ELSE 0 END isAnagram 
FROM (
    SELECT id, letter, COUNT(letter) as c 
    FROM @table 
    WHERE id = 1 
    GROUP BY id, letter)as t 
FULL OUTER JOIN (
    SELECT id, letter, COUNT(letter) as c 
    FROM @table 
    WHERE id = 2 
    GROUP BY id, letter) as p 
    ON t.letter = p.letter and t.c =p.c 
WHERE t.letter is NULL OR p.letter is null 

Ausgabe:

isAnagram 
0 
+0

Ah, Zählung hinzufügen, um mit doppelten Buchstaben zu kämpfen :) – gofr1

+0

** Ja, jetzt funktioniert es! ;) ** –

+0

Danke für die Überprüfung :) Ich habe ein paar richtige Wörter verwendet und es gab ok (natürlich) – gofr1

2

Sie können Schleifen auch in Funktionen verwenden und sie können schnell arbeiten. Ich bin nicht in der Lage eine der anderen Antworten zu erhalten einmal in der Nähe der Leistung dieser Funktion:

CREATE FUNCTION IsAnagram 
(
    @value1 VARCHAR(255) 
    , @value2 VARCHAR(255) 
) 
RETURNS BIT 
BEGIN 

    IF(LEN(@value1) != LEN(@value2)) 
     RETURN 0; 

    DECLARE @firstChar VARCHAR(3); 

    WHILE (LEN(@value1) > 0) 
    BEGIN 
     SET @firstChar = CONCAT('%', LEFT(@value1, 1), '%'); 

     IF(PATINDEX(@firstChar, @value2) > 0) 
      SET @value2 = STUFF(@value2, PATINDEX(@firstChar, @value2), 1, ''); 
     ELSE 
      RETURN 0; 

     SET @value1 = STUFF(@value1, 1, 1, ''); 

    END 

    RETURN (SELECT IIF(@value2 = '', 1, 0)); 

END 

GO 

SELECT dbo.IsAnagram('asd', 'asd') 
--1 
SELECT dbo.IsAnagram('asd', 'dsa') 
--1 
SELECT dbo.IsAnagram('assd', 'dsa') 
--0 
SELECT dbo.IsAnagram('asd', 'dssa') 
--0 
SELECT dbo.IsAnagram('asd', 'asd') 
+1

Getestet, funktioniert, upvote! ;) –

2

Dies etwas ist eine Zahlen-Tabelle mit helfen kann.

Code zum Erstellen und Füllen einer Tabelle mit kleinen Nummern ist unten.

CREATE TABLE dbo.Numbers 
    (
    Number INT PRIMARY KEY 
); 

WITH Ten(N) AS 
(
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
) 
INSERT INTO dbo.Numbers 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID) AS Number 
FROM Ten T10, 
     Ten T100, 
     Ten T1000 

Sobald das an Ort und Stelle ist, können Sie

SELECT W1, 
     W2, 
     IsAnagram = CASE 
        WHEN LEN(W1) <> LEN(W2) 
         THEN 0 
        ELSE 
         CASE 
         WHEN EXISTS (SELECT SUBSTRING(W1, Number, 1), 
              COUNT(*) 
             FROM dbo.Numbers 
             WHERE Number <= LEN(W1) 
             GROUP BY SUBSTRING(W1, Number, 1) 
             EXCEPT 
             SELECT SUBSTRING(W2, Number, 1), 
              COUNT(*) 
             FROM dbo.Numbers 
             WHERE Number <= LEN(W2) 
             GROUP BY SUBSTRING(W2, Number, 1)) 
          THEN 0 
         ELSE 1 
         END 
        END 
FROM (VALUES 
     ('CAT', 'ACT'), 
     ('CAR', 'RAC'), 
     ('BUZ', 'BUS'), 
     ('FUZZY', 'MUZZY'), 
     ('PACK', 'PACKS'), 
     ('AA', 'AA'), 
     ('ABCDEFG', 'GFEDCBA')) T(W1, W2) 

Oder eine alternative Implementierung könnte

IsAnagram = CASE 
       WHEN LEN(W1) <> LEN(W2) 
        THEN 0 
       ELSE 
        CASE 
        WHEN EXISTS (SELECT 1 
            FROM dbo.Numbers N 
             CROSS APPLY (VALUES(1,W1), 
                  (2,W2)) V(Col, String) 
            WHERE N.Number <= LEN(W1) 
            GROUP BY SUBSTRING(String, Number, 1) 
            HAVING COUNT(CASE WHEN Col = 1 THEN 1 END) <> 
             COUNT(CASE WHEN Col = 2 THEN 1 END)) 
         THEN 0 
        ELSE 1 
        END 
       END