SELECT 子句中的子查询

Posted

技术标签:

【中文标题】SELECT 子句中的子查询【英文标题】:Subqueries in SELECT clause 【发布时间】:2014-02-23 01:06:59 【问题描述】:

我需要从多个表中SELECT 来获取如下结果表:

+--------+-------+-------------------+----------------------+
| itemID | level | studyPhraseString | meaningPhraseStrings |
+--------+-------+-------------------+----------------------+
| 1      | 4     | la maison         | house                |
+--------+-------+-------------------+----------------------+
| 2      | 3     | voler             | to fly,to steal      |
+--------+-------+-------------------+----------------------+

注意:studyPhraseStringmeaningPhraseStrings 应该是由 word 表中的值组成的串联字符串。

我的桌子:

项目

CREATE TABLE `item` (
`itemID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
`groupID` bigint(11) unsigned NOT NULL,
`studyLang` varchar(5) NOT NULL,
`meaningLang` varchar(5) NOT NULL,
`studyPhraseID` bigint(11) unsigned NOT NULL,
PRIMARY KEY (`itemID`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;

意义

CREATE TABLE `meaning` (
  `meaningID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `itemID` bigint(11) unsigned NOT NULL,
  `meaningPhraseID` bigint(11) unsigned NOT NULL,
  `meaningIndex` int(11) unsigned NOT NULL,
  PRIMARY KEY (`meaningID`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=latin1;

短语

CREATE TABLE `phrase` (
  `phraseID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `phraseLang` varchar(5) NOT NULL DEFAULT '',
  PRIMARY KEY (`phraseID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;

phrase_word

CREATE TABLE `phrase_word` (
  `phrase_wordID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `phraseID` bigint(11) unsigned NOT NULL,
  `wordID` bigint(11) unsigned NOT NULL,
  `wordIndex` int(11) unsigned NOT NULL,
  PRIMARY KEY (`phrase_wordID`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;

状态

CREATE TABLE `status` (
  `statusID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `itemID` bigint(11) unsigned NOT NULL,
  `level` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `nextReviewTime` int(11) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`statusID`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;

单词

CREATE TABLE `word` (
  `wordID` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `string` varchar(64) NOT NULL DEFAULT '',
  PRIMARY KEY (`wordID`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

我已经编写了以下 SELECT 语句:

SELECT item.itemID, status.level, 
(SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ')
FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID 
WHERE subItem.itemID=item.itemID
GROUP BY subItem.itemID
) AS studyPhraseString
FROM item INNER JOIN status ON item.itemID=status.itemID
WHERE item.groupID=5
ORDER BY status.statusID DESC

这有效,但不包括meaningPhraseString。我不知道如何将单词连接成短语并将短语连接成一个字符串,用 , 分隔

我尝试了嵌套的 GROUP_CONCAT 子句,但没有成功(子查询返回超过 1 行):

问题

应如何编写此语句以包含meaningPhraseStrings?提前致谢。

PS:我希望这是一个单一的查询

我尝试了以下方法,但失败了。为什么?它有两个级别的相关查询。

SELECT 
item.itemID, 
status.level, 
(
    SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ')
    FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
    INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID 
    WHERE subItem.itemID=item.itemID
    GROUP BY subItem.itemID
) AS studyPhraseString, 

(
    SELECT GROUP_CONCAT(meaningPhraseString SEPARATOR '.') 
    FROM (
        (
            SELECT GROUP_CONCAT(word.string ORDER BY phrase_word.wordIndex SEPARATOR ' ') AS meaningPhraseString
            FROM word INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
            INNER JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
            INNER JOIN item AS subItem ON meaning.itemID=subItem.itemID 
            
            WHERE subItem.itemID=item.itemID #This fails
            
            GROUP BY meaning.meaningPhraseID
        ) AS meaningPhraseString
    )
) AS meaningPhraseStrings

FROM item INNER JOIN status ON item.itemID=status.itemID
WHERE item.groupID=5
ORDER BY item.itemID DESC

样本数据:

INSERT INTO `status` VALUES (22,22,0,0),(23,23,0,0),(24,25,0,0),(25,24,0,0),(26,26,0,0);
INSERT INTO `item` VALUES (22,5,'fr','en',49),(23,5,'fr','en',48),(24,5,'fr','en',56),(25,5,'fr','en',50),(26,5,'fr','en',57);
INSERT INTO `meaning` VALUES (27,22,51,0),(28,23,52,0),(29,23,54,1),(30,24,59,0),(31,24,61,1),(32,25,53,0),(33,25,55,1),(34,26,58,0),(35,26,60,1);
INSERT INTO `phrase` VALUES (48,'fr'),(49,'fr'),(50,'fr'),(51,'en'),(52,'en'),(53,'en'),(54,'en'),(55,'en'),(56,'fr'),(57,'fr'),(58,'en'),(59,'en'),(60,'en'),(61,'en');
INSERT INTO `word` VALUES (46,'l\'autobus'),(47,'bus'),(48,'pourquoi'),(49,'comment'),(50,'why'),(51,'ça'),(52,'va?'),(53,'voler'),(54,'incroyable'),(55,'how'),(56,'is'),(57,'to'),(58,'are'),(59,'incredible'),(60,'that?'),(61,'you?'),(62,'fly'),(63,'amazing'),(64,'hi'),(65,'steal');
INSERT INTO `phrase_word` VALUES (86,49,46,0),(87,51,47,0),(88,48,48,0),(89,50,49,0),(90,52,50,0),(91,54,50,0),(92,50,51,1),(93,50,52,2),(94,57,53,0),(95,53,55,0),(96,56,54,0),(97,54,56,1),(98,53,58,1),(99,58,57,0),(100,59,59,0),(101,54,60,2),(102,53,61,2),(103,58,62,1),(104,61,63,0),(105,60,57,0),(106,55,64,0),(107,60,65,1);

最终答案

SELECT i.itemID, 
        s.level,
        sp.studyPhraseString,
        GROUP_CONCAT(mp.meaningPhraseString
                     SEPARATOR ', ') AS meaningPhraseStrings            
   FROM item AS i
   JOIN meaning AS m ON i.itemID = m.itemID
   JOIN status AS s ON i.itemID = s.itemID
   JOIN ( 
       SELECT subItem.studyPhraseID,
              GROUP_CONCAT(word.string 
                           ORDER BY phrase_word.wordIndex 
                           SEPARATOR ' ') AS studyPhraseString
         FROM word 
         JOIN phrase_word
           ON word.wordID=phrase_word.wordID
         JOIN item AS subItem 
           ON phrase_word.phraseID=subItem.studyPhraseID 
     GROUP BY subItem.studyPhraseID
        ) AS sp ON i.studyPhraseID = sp.studyPhraseID   
   JOIN ( 
       SELECT meaning.meaningPhraseID,
              GROUP_CONCAT(word.string 
              ORDER BY phrase_word.wordIndex
              SEPARATOR ' ') AS meaningPhraseString
         FROM word 
         JOIN phrase_word ON word.wordID=phrase_word.wordID
         JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
         JOIN item AS subItem ON meaning.itemID=subItem.itemID 
     GROUP BY meaning.meaningPhraseID
        ) AS mp ON m.meaningPhraseID = mp.meaningPhraseID
  GROUP BY i.itemID, s.level, sp.studyPhraseString
  ORDER BY i.itemID, s.level, sp.studyPhraseString

【问题讨论】:

GROUP_CONCAT() 在查询中几乎没有意义,除非伴随着 GROUP BY 子句。 我已将 GROUP BY 添加到子查询中,但没有任何区别。我同意应该有一个 GROUP BY。 我正在尝试回答您的问题,但我不太了解您的架构。能否提供一些示例表数据? 【参考方案1】:

您的问题似乎是这样的:

如何将单词连接成短语并将短语连接成一个字符串

让我们分解一下。您需要将五个表连接在一起。其中三个是物理表,分别是itemmeaningstatus。从这些表中,您可以获得对您需要的名为 itemID 和 level 的结果集项目的引用,并获得项目之间的关系及其含义。

您需要的另外两个表是虚拟表(即子查询)。其中一个为您提供法语短语,另一个为您提供英语翻译。

让我们为虚拟表创建两个查询。让我们先把单词变成短语。像这样的查询可以实现该目标。

 SELECT subItem.studyPhraseID,
        GROUP_CONCAT(word.string 
                     ORDER BY phrase_word.wordIndex 
                     SEPARATOR ' ') AS studyPhraseString
   FROM word 
  INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
  INNER JOIN item AS subItem ON phrase_word.phraseID=subItem.studyPhraseID 
  GROUP BY subItem.studyPhraseID

这将为您提供短语 ID 号和短语的结果集表。这是一个基于您的示例的 SQL 小提琴。 http://sqlfiddle.com/#!2/11ae2/9/0

然后,创建一个类似的查询,为您提供 meaningPhraseString 值。

 SELECT meaning.meaningPhraseID,
        GROUP_CONCAT(word.string 
        ORDER BY phrase_word.wordIndex
        SEPARATOR ' ') AS meaningPhraseString
   FROM word 
  INNER JOIN phrase_word ON word.wordID=phrase_word.wordID
  INNER JOIN meaning ON phrase_word.phraseID=meaning.meaningPhraseID
  INNER JOIN item AS subItem ON meaning.itemID=subItem.itemID 
  GROUP BY meaning.meaningPhraseID

这给出了 id 和含义短语的列表。这是小提琴。 http://sqlfiddle.com/#!2/11ae2/6/0

因此,我们将需要一个五向连接(三个物理表和两个子查询)来获得我们的最终结果集。总之,它看起来像这样:

 SELECT i.itemID, 
        s.level,
        sp.studyPhraseString,
        mp.meaningPhraseString            
   FROM item AS i
   JOIN meaning AS m ON i.itemID = m.itemID
   JOIN status AS s ON i.itemID = s.itemID
   JOIN ( 
           /* the studyPhrase subquery */ 
        ) AS sp ON i.studyPhraseID = sp.studyPhraseID   
   JOIN ( 
           /* the meaningPhrase subquery */ 
        ) AS mp ON m.meaningPhraseID = mp.meaningPhraseID

这里的诀窍是您可以交替使用查询(或虚拟表)和物理表。因此,当您需要汇总一堆表时,您可以创建一个查询来执行此操作,然后将其粘贴到 JOIN (/*query*/) AS alias

最后,您需要通过在查询中添加另一个GROUP_CONCAT()GROUP BY 来创建逗号连接的字符串(例如to fly, to steal)。那么最终结果是

 SELECT i.itemID, 
        s.level,
        sp.studyPhraseString,
        GROUP_CONCAT(mp.meaningPhraseString
                     SEPARATOR ', ') AS meaningPhraseStrings            
   FROM item AS i
   JOIN meaning AS m ON i.itemID = m.itemID
   JOIN status AS s ON i.itemID = s.itemID
   JOIN ( 
           /* the studyPhrase subquery */ 
        ) AS sp ON i.studyPhraseID = sp.studyPhraseID   
   JOIN ( 
           /* the meaningPhrase subquery */ 
        ) AS mp ON m.meaningPhraseID = mp.meaningPhraseID
  GROUP BY i.itemID, s.level, sp.studyPhraseString
  ORDER BY i.itemID, s.level, sp.studyPhraseString

这就是您的查询。 http://sqlfiddle.com/#!2/11ae2/16/0 它确实利用了结构化查询语言中的结构化

【讨论】:

谢谢,我稍后会用示例数据更新问题。 我现在已经为meaningPhraseString 编写了一个查询。我想以更好的方式在这里展示我的架构,但我想不出一个清晰的方式来表达它?我应该发布图片吗? 我尝试将查询组合在一起,但没有运气。请你看看我的尝试并帮助我。 样本数据?请问? 我为示例数据和数据库导出添加了插入语句。很抱歉花了这么长时间。

以上是关于SELECT 子句中的子查询的主要内容,如果未能解决你的问题,请参考以下文章

View 的 SELECT 包含 FROM 子句中的子查询

SQL 编译错误:无法评估不受支持的子查询类型 - SELECT 子句中的函数调用

带有联合错误的 MySQL 视图 - “视图的 SELECT 包含 FROM 子句中的子查询”

MYSQL 获取最低值的记录 | View 的 SELECT 包含 FROM 子句中的子查询

MySQL—— 子查询

SQL中的子查询