【问题标题】:SQL Retrieve data with more than 1 recordSQL 检索多于 1 条记录的数据
【发布时间】:2016-06-06 17:14:12
【问题描述】:

我遇到了“不是 GROUP BY 表达式”错误。

我需要搜索类似的标题、媒介和描述。 艺术家表由艺术家 ID、艺术家姓氏和艺术家名字组成。 工作表由 workid、title、medium、description、artistid 组成 艺术家是唯一的关键。

列出在数据库中记录了不止一个副本的任何艺术品的详细信息。

SELECT W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W, dtoohey.artist A
GROUP BY W.artistid, A.FirstName, A.LastName 
having count(*) > 1;

【问题讨论】:

  • 也许确切的错误信息会有所帮助,否则having count(*) > 1 对我来说似乎不合适,但这可能意味着我根本没有那样使用它。
  • 你是从作品中选择,从艺术家中选择,两者没有关系?你的意思是加入他们,也许是艺术家? (你明白我的问题吗?)另外,请编辑你的帖子以删除不适用的标签。这被标记为 MySQL 和 Oracle,这可能是不正确的。
  • 在我的小世界里,模型中总有一种关系将表格建模的事实联系起来,比如说在这种情况下(按原样命名)每个艺术家都会与 0 个或多个作品相关项目。因此,拥有(在更规范化的系统中)第三个表可能会有所帮助,该表仅记录 artid 与 workid 的关系。比回答这类问题更容易。非规范化,工作表可能携带一个外键引用到艺术家.artistid,然后对这些表的写操作将被链接到事务中,以确保关系的完整性。有关系吗?
  • 很抱歉 concat of to cmets,但编辑时间已经结束,因此:尝试将您的艺术家 ID 作为连接标准。然而,当一件作品与更多艺术家相关联时,第三种关系可能会派上用场。

标签: sql database


【解决方案1】:

似乎你没有正确的表之间的连接(我建议一个..你应该做正确的一个)

如果您希望 W.artistIid 上的分组依据(别名为每个艺术家的 workid 的计数),您不能在 select 中包含 W.workid、W.title、W.medium

SELECT  W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W
INNER JOIN dtoohey.artist A ON A.artistid = W.artistid
GROUP BY W.artistid, A.FirstName, A.LastName 
having count(*) > 1;

否则,如果要检查选择是否为列选择返回多行,则必须将所有列添加到 group by 子句

SELECT W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W
INNER JOIN dtoohey.artist A ON A.artistid = W.artistid
GROUP BY W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName 
having count(*) > 1;

【讨论】:

  • 认为这两个表可能需要连接?
  • @mathguy 可能是 ..(或不是)问题是关于 group by .. 并且 group by 也适用于笛卡尔积 .. 在两个表之间.. 问题基本相同..有或没有加入..
  • 问题是关于在数据库中有多个副本的作品。这怎么可能与笛卡尔积有关?认真点好吗?
  • 如果对笛卡尔积 having count(*) > 1 进行分组没有任何意义,因为所有记录的计数>1 ... 一个例外是极端情况,其中最多有一条记录这两个表。
  • 我已经为 JOIN 添加了一个建议 .. 所以 OP 可以正确评估
【解决方案2】:

也许写一个答案(侧重于@mathguy 对缺失连接特异性的观察,以及 SELECT 列与 GROUP BY/HAVING 的混合不适合)更好地找出您的问题并给出想法如何加强问题 ;-) ...下次我建议对这个问题更加重视,这样世界就不必在答案上如此繁重了。

我不认为这是 MySQL、Oracle 或不是数据库特定的问题,而是更多 SQL 初学者的学习之旅……您可能还想在这里寻找连接解释:"Difference between Inner Join & Full join"

从问题中给出的最小信息子集开始:2 表艺术家和作品相关的推测可能通过共享 ID(比如艺术家 ID)。

在 PostgreSQL 或 ParStream 等数据库中直接提出问题的一件事是选择分组中的列,查询既不分组也不聚合/过滤。但是我们开始了:

创建表:

$psql> CREATE TABLE artist(artist_id INT, given_name VARCHAR(42), family_name VARCHAR(99));
CREATE TABLE
$psql> CREATE TABLE work(work_id INT, artist_id INT, title VARCHAR(42));
CREATE TABLE

插入一些数据:

$psql> INSERT INTO artist VALUES(1, 'John', 'Doe');
INSERT 0 1
$psql> INSERT INTO artist VALUES(2, 'Natalie', 'Noir');
INSERT 0 1
$psql> INSERT INTO work VALUES(43, 1, 'The game is on');
INSERT 0 1
$psql> INSERT INTO work VALUES(44, 1, 'The game is over');
INSERT 0 1
$psql> INSERT INTO work VALUES(98, 2, 'La nuit commonce');
INSERT 0 1
$psql> INSERT INTO work VALUES(97, 2, 'Un jour se lve');
INSERT 0 1

检查里面有什么:

$psql> SELECT * FROM work;
 work_id | artist_id |      title       
---------+-----------+------------------
      43 |         1 | The game is on
      44 |         1 | The game is over
      98 |         2 | La nuit commonce
      97 |         2 | Un jour se lve
(4 rows)

$psql> SELECT * FROM artist;
 artist_id | given_name | family_name 
-----------+------------+-------------
         1 | John       | Doe
         2 | Natalie    | Noir
(2 rows)

显示隐式 INNER JOIN:

$psql> SELECT * FROM work W, artist A;
 work_id | artist_id |      title       | artist_id | given_name | family_name 
---------+-----------+------------------+-----------+------------+-------------
      43 |         1 | The game is on   |         1 | John       | Doe
      43 |         1 | The game is on   |         2 | Natalie    | Noir
      44 |         1 | The game is over |         1 | John       | Doe
      44 |         1 | The game is over |         2 | Natalie    | Noir
      98 |         2 | La nuit commonce |         1 | John       | Doe
      98 |         2 | La nuit commonce |         2 | Natalie    | Noir
      97 |         2 | Un jour se lve   |         1 | John       | Doe
      97 |         2 | Un jour se lve   |         2 | Natalie    | Noir
(8 rows)

显示带有虚拟条件的显式 INNER JOIN 以让解析器通过我们的查询(更新:不要在家里使用它,只是为了显示混搭。):

$psql> SELECT * FROM work W INNER JOIN artist A ON 1 = 1;
 work_id | artist_id |      title       | artist_id | given_name | family_name 
---------+-----------+------------------+-----------+------------+-------------
      43 |         1 | The game is on   |         1 | John       | Doe
      43 |         1 | The game is on   |         2 | Natalie    | Noir
      44 |         1 | The game is over |         1 | John       | Doe
      44 |         1 | The game is over |         2 | Natalie    | Noir
      98 |         2 | La nuit commonce |         1 | John       | Doe
      98 |         2 | La nuit commonce |         2 | Natalie    | Noir
      97 |         2 | Un jour se lve   |         1 | John       | Doe
      97 |         2 | Un jour se lve   |         2 | Natalie    | Noir
(8 rows)

现在一个更有用的 INNER JOIN 仅匹配两个表中的这些条目,它们通过“创建者”关系相关:

$psql> SELECT * FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;
 work_id | artist_id |      title       | artist_id | given_name | family_name 
---------+-----------+------------------+-----------+------------+-------------
      43 |         1 | The game is on   |         1 | John       | Doe
      44 |         1 | The game is over |         1 | John       | Doe
      98 |         2 | La nuit commonce |         2 | Natalie    | Noir
      97 |         2 | Un jour se lve   |         2 | Natalie    | Noir
(4 rows)

所以上面我们盲目地相信数据管理部分会神奇地输入artist_id 值总是正确并符合我们的期望(在现实生活中,REFERENCES 外键约束肯定会放在工作表中的列上(没有艺术家/创作者就没有工作会规定艺术家表是因果关系的“第一”)。

您还看到,从表列表中进行选择与没有任何约束的 INNER JOIN 相同,即表中所有条目的笛卡尔积与表艺术家中的所有条目一起使用。

现在您的查询(为最小表模型编辑了一点)除了我在其请求想法中不清楚之外,还出现了错误,如本答案文本顶部所述:

$psql>  SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.family_name FROM work W, artist A GROUP BY W.artist_id, A.given_name, A.family_name HAVING COUNT(*) > 1;

ERROR:  column "w.work_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.fam...

这当然不能通过使用更有意义的连接输入集来解决(claro,因为错误指向 select 和 group by 列表中的不匹配:

$psql> SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id GROUP BY W.artist_id, A.given_name, A.family_name HAVING COUNT(*) > 1;

ERROR:  column "w.work_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.fam...

您需要就您想要达到的目标提出建议,以获得单一答案(有效)。在您这样做之前,这里有一些产品:

鉴于您仅加入现有的艺术家和作品 ID 对,您不需要该拥有子句,因为不存在的艺术家、缺失的作品或缺失的艺术家和作品的组合都不会进入您的查询所针对的行集,所以:

$psql> SELECT title, R.* FROM ( SELECT W.work_id AS work_id_filtered, W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id GROUP BY W.work_id, W.artist_id, A.given_name, A.family_name) R INNER JOIN work WW ON WW.work_id = R.work_id_filtered;
      title       | work_id_filtered | artist_id | given_name | family_name 
------------------+------------------+-----------+------------+-------------
 The game is on   |               43 |         1 | John       | Doe
 The game is over |               44 |         1 | John       | Doe
 La nuit commonce |               98 |         2 | Natalie    | Noir
 Un jour se lve   |               97 |         2 | Natalie    | Noir
(4 rows)

这应该会让您有点笨拙,但对于我周日早上的所有标题(非分组文件)的漂亮列表以及来自内部查询的分组字段来说已经足够了。格式化查询可以写成:

SELECT title,
       R.*
FROM
  (SELECT W.work_id AS work_id_filtered,
                       W.artist_id,
                       A.given_name,
                       A.family_name
   FROM
   work W
   INNER JOIN artist A ON W.artist_id = A.artist_id
   GROUP BY W.work_id,
            W.artist_id,
            A.given_name,
            A.family_name) R
INNER JOIN
work WW ON WW.work_id = R.work_id_filtered;

删除任何 GROUP BY(直到问题详细说明为什么任务需要它):

$psql> SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;

 work_id |      title       | artist_id | given_name | family_name 
---------+------------------+-----------+------------+-------------
      43 | The game is on   |         1 | John       | Doe
      44 | The game is over |         1 | John       | Doe
      98 | La nuit commonce |         2 | Natalie    | Noir
      97 | Un jour se lve   |         2 | Natalie    | Noir
(4 rows)

查询格式化为不必水平滚动:

SELECT W.work_id, W.title,  W.artist_id, A.given_name, A.family_name 
FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;

注意:是的,正如@ThorstenKettner 正确指出的那样,我编造了“FULL INNER JOIN”这个词,对不起。也许我的大脑需要笛卡尔填充来平衡 LEFT|RIGHT|FULL OUTER JOIN - 谁知道 ;-)

【讨论】:

  • 我认为FULL INNER JOIN这个词是你编出来的。至少我这辈子从没听说过这个。您展示的是CROSS JOIN。并且使用INNER JOIN ON 1=1 只是混淆了问题,不应该使用。
  • 感谢@ThorstenKettner,所以是的,实际上是的,但不是正式的;-) 是的(但走私通常取决于混淆)更新了答案。再次感谢您的反馈。
【解决方案3】:

我使用了 scaisEdge 答案并进行了编辑。我意识到如果我选择 workid(唯一键),我将无法显示任何内容,但没有它,我的所有字段都已显示。

【讨论】:

    【解决方案4】:

    如前所述,问题主要在于您尚未完全了解自己在做什么。

    第一点是您的加入。通过仅用逗号分隔表格,您使用的语法在 20 多年前就变得多余了。作为初学者,您似乎很少使用它。您一定是在一本非常古老的书或教程中找到的。简而言之:不要像这样加入表格。使用显式连接。逗号表示CROSS JOIN。所以你有的是:

    FROM dtoohey.work W CROSS JOIN dtoohey.artist A
    

    这意味着您将每位艺术家与每件作品结合在一起。这很可能不是您想要的。您想加入相关艺术家和作品。您的查询显示您的工作表中有一个artistid,因此您的模型中的一位艺术家制作了一件作品。因此,适当的联接将是:

    FROM dtoohey.work w 
    INNER JOIN dtoohey.artist a ON a.artistid = w.artistid
    

    第二点是您正在聚合行。 GROUP BY W.artistid, A.FirstName, A.LastName 告诉 DBMS 聚合这些行,这样您就可以为每位艺术家获得一个结果行。对于having count(*) > 1,您说您只想要拥有不止一件作品的艺术家。但是在您的 select 子句中,您正在展示一件作品 (W.workid, W.title, W.medium, W.description)。哪一个?如果您只展示一位艺术家的一排,并且每位艺术家都有不止一件作品,您会展示哪位艺术家的作品? DBMS 注意到您忘记告诉它选择什么并引发错误。现在您可能会同意,带有给定 GROUP BY 和 HAVING 子句的查询没有意义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-13
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多