【问题标题】:How to query for a phrase on SQL database of words?如何在 SQL 单词数据库中查询一个短语?
【发布时间】:2017-11-28 21:18:58
【问题描述】:

我正在使用MySQL,我有一个包含歌曲的 SQL 数据库,其中包含一个包含 8 列歌曲单词信息的表。每行代表歌曲歌词中的一个单词:

  1. songSerial - 歌曲序号

  2. songName - 歌曲名称

  3. word - 歌词中的一个词

  4. row_number - 找到单词的行号

  5. word_position_in_row - 单独一行中单词的个数

  6. house_number - 单词所属的房子编号

  7. house_row - 在房子里找到这个词的行数

  8. word_number - 所有歌曲歌词中单词的个数

行示例:{ 4 , The Scientist , secrets , 8 , 4 , 2 , 1 , 37 }

现在我要查询所有包含一组单词的歌曲。例如所有有句子的单词:“我爱你”。 必须按照这个顺序,而不是来自不同的行或房子。

以下是我的 oneDrive 中用于创建数据库和大约 400 行的脚本: TwoTextScriptFilesAndTheirZip

谁能帮忙?

谢谢

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    一种方法是使用joins:

    select s.*
    from songwords sw1 join
         songwords sw2
         on sw2.songSerial = sw1.songSerial and
            sw2.word_number = sw1.word_number + 1 join
         songwords sw3
         on sw3.songSerial = sw2.songSerial and
            sw3.word_number = sw2.word_number + 1 
    where sw1.word = 'I' and sw2.word = 'love' and sw3.word = 'you';
    

    或者,如果您愿意:

    where concat_ws(' ', sw1.word, sw2.word, sw3.word) = 'I love you'
    

    从优化的角度来看,情况更糟(使用word 的索引对性能没有帮助),但很清楚查询在做什么。

    这种类型的搜索建议使用full text index。唯一需要注意的是,无论长度如何,您都需要删除停用词列表并索引所有单词。 (“我”和“你”是停用词的典型例子。)

    【讨论】:

    • 我们可能想使用word_position_in_row 代替word_number,并且在匹配条件中还包括house_numberrow_number(根据规范“不是来自不同的行或房子”) .这些连接需要有合适的索引可用。
    • 感谢您的查询成功。我知道这就像蛮力,但对我来说已经足够了(我希望哈哈)。我还添加了 row_number critirea 所以感谢您的建议!
    【解决方案2】:

    这对于大表来说是一种昂贵的方法,假设word 不为空,我们可以这样做:

    SET group_concat_max_len = 16777216 ;
    
    SELECT t.song_serial
         , t.house_number
         , t.row_number
      FROM mytable t
     GROUP
        BY t.songserial
         , t.house_number
         , t.row_number
    HAVING CONCAT(' ',GROUP_CONCAT(t.word ORDER BY t.word_position_by_row),' ') 
      LIKE CONCAT('% ','I love you',' %')
    

    我们肯定想要一个合适的可用索引,例如

    ... ON `mytable` (`songserial`,`house_number`,`row_number`,`word`) 
    

    如果短语中的某个词不常用,我们可以先通过搜索该不常用词来优化一点,然后在同一行中获取所有词...

    SELECT t.song_serial
         , t.house_number
         , t.row_number
      FROM ( SELECT r.songserial
                  , r.house_number
                  , r.row_number
               FROM mytable r
              WHERE r.word = 'love'
              GROUP
                 BY r.word
                  , r.songserial
                  , r.house_number
                  , r.row_number
           ) s
      JOIN mytable t 
        ON t.songserial   = s.songserial 
       AND t.house_number = s.house_number
       AND t.row_number   = s.row_number
     GROUP
        BY t.songserial
         , t.house_number
         , t.row_number
    HAVING CONCAT(' ',GROUP_CONCAT(t.word ORDER BY t.word_position_by_row),' ') 
      LIKE CONCAT('% ','I love you',' %')
    

    内联视图 s 将受益于以 word 作为前导列的覆盖索引

    ... ON `mytable` (`word`,`songserial`,`house_number`,`row_number`)
    

    【讨论】:

      【解决方案3】:

      你寻找这些词和相关搜索位置:1 = 我,2 = 爱,3 = 你。让我们将它们与两条歌词进行比较:

      我爱,爱,爱你 实际位置:1 2 3 4 5 6 搜索位置:- 1 2 2 2 3 差异:- 1 1 2 3 3 我想你,我爱你 实际位置:1 2 3 4 5 6 7 搜索位置:1 - 3 - 1 2 3 差异:0 - 0 - 4 4 4

      如果我们查看第一行的位置增量,我们会得到 1(两次)、2(一次)和 3(两次)。

      对于第二行,我们得到增量 0(两次)和 4(三次)。

      因此,对于第二首歌曲行,我们会找到与搜索词一样多的匹配项的增量,而对于第一行则没有。第二行是一个匹配项。

      这里是查询。我假设我们有一个临时表 search 填充了搜索词和相对位置以提高可读性。

      select distinct w.songserial, w.songname, w.house_number
      from words w
      join search s on s.word = w.word
      group by
        w.songserial, w.songname, w.row_number, w.house_number, w.house_row, -- song line
        w.word_position_in_row - s.pos -- delta
      having count(*) = (select count(*) from search);
      

      此查询基于:

      • 一首歌由songserial + songname + house_number标识
      • 歌曲行由songserial + songname + row_number + house_number + house_row 标识

      这可能是错误的;我不知道关于一首歌的房子和门牌号码是什么意思。但这很容易调整。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-24
        • 1970-01-01
        • 2019-10-14
        • 2016-09-07
        • 1970-01-01
        • 2016-11-02
        • 1970-01-01
        相关资源
        最近更新 更多