使用sql server检查字谜

时间:2016-08-05 09:54:44

标签: sql sql-server database

ACT CAT 是字谜

我必须在sql server中编写一个函数,它接受2个字符串并给出一个布尔输出,指示它们是否都是字谜。

这在sql server中没有意义,但是,它仅用于学习目的

5 个答案:

答案 0 :(得分:8)

SQL Server并不擅长此类事情,但在这里你是:

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

结果:

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

答案 1 :(得分:4)

首先将两个单词拆分(T-SQL Split Word into characters)到临时表中。然后执行外连接并检查空值。

感谢George的评论:

  1. 将两个单词拆分为T-SQL Split Word into characters个临时表
  2. 修改临时表或使用CTE添加带有count(*)和group by letters子句
  3. 的列
  4. 使用字母在两个临时表上执行完全外连接,并在连接条件中计数
  5. 检查输出中的空值 - 如果没有,则表示你有一个字谜

答案 2 :(得分:2)

第一个想到我的想法:

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 @n=@n-1
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

输出:

isAnagram
0

答案 3 :(得分:2)

您还可以在函数中使用循环,它们可以快速工作。即使接近此功能的性能,我也无法获得任何其他答案:

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')

答案 4 :(得分:2)

这是数字表可以帮助的东西。

创建并填充小数字表的代码如下。

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

一旦到位,您可以使用

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)

或者替代实施可能是

   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