错误:列“字段”必须出现在 GROUP BY 子句中或用于聚合函数中

Imr*_*ran 2 mysql postgresql migration

我遇到了一个让我有点困惑的 SQL 问题。关于这个问题,我有两个不同的问题:

  • 为什么?
  • 我该如何解决?

以下查询:

SELECT subs.date_added,subs.subscribed_to,users.userid, users.username, users.email, users.avatar, users.fbuid, users.level, users.avatar_url, users.sex, users.dob, users.profile_hits, users.total_videos, users.subscribers, users.doj, users.extras, users.first_name, users.last_name, users.ban_status, users.usr_status, users.last_logged, users.country, users.user_filter_level, users.signup_ip, subs.userid as subscriber 
FROM cb_subscriptions as subs 
     LEFT JOIN cb_users AS users 
     ON subs.userid=users.userid 
WHERE subs.subscribed_to ='2960' 
GROUP BY subs.subscribed_to ,subs.userid 
ORDER BY subs.date_added ASC 
LIMIT 8;
Run Code Online (Sandbox Code Playgroud)

...产生以下错误:

ERROR: column "users.userid" must appear in the GROUP BY clause or be used in an aggregate function
Run Code Online (Sandbox Code Playgroud)

该字段是 a CHAR,所以我不确定要使用哪种聚合,或者(对我的理解更重要)为什么需要一个聚合。

正如我所说,我不确定我是否理解为什么会发生这种情况。我假设我group by不像我想象的那样理解;)

这不是我的查询,我正在将在 MySQL 中构建的系统原型转换为 PostgreSQL。这个查询确实在MySQL的工作。这是否构成了 MySql 中的错误,或者 PostgreSQL 的缺点,或者只是解释的不同?

当我在group by子句中添加此列时,它起作用了,但我不明白为什么?

工作查询

SELECT subs.date_added, users.userid, users.username, users.email, 
       users.avatar, users.fbuid, users.level, users.avatar_url, 
       users.sex, users.dob, users.profile_hits, users.total_videos,
       users.subscribers, users.doj, users.extras, users.first_name, 
       users.last_name, users.ban_status, users.usr_status, 
       users.last_logged, users.country, users.user_filter_level, 
       users.signup_ip, subs.userid as subscriber 
FROM cb_subscriptions as subs 
     LEFT JOIN cb_users AS users 
     ON subs.userid=users.userid 
WHERE subs.subscribed_to ='2960' 
GROUP BY users.userid, 
         subs.subscribed_to, 
         subs.userid, 
         subs.date_added 
ORDER BY subs.date_added ASC 
LIMIT 8;
Run Code Online (Sandbox Code Playgroud)

cb_subscriptions DDL

CREATE TABLE `cb_subscriptions` (
    `subscription_id` int(225) NOT NULL,
    `userid` int(11) NOT NULL,
    `subscribed_to` mediumtext NOT NULL,
    `date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

ALTER TABLE `cb_subscriptions`
  ADD PRIMARY KEY (`subscription_id`);

ALTER TABLE `cb_subscriptions`
  MODIFY `subscription_id` int(225) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1
Run Code Online (Sandbox Code Playgroud)

cb_users DDL

CREATE TABLE `cb_users` (
  `userid` bigint(20) NOT NULL,
  `fbuid` varchar(225) NOT NULL,
  `category` int(20) NOT NULL,
  `username` varchar(50) NOT NULL,
  `first_name` varchar(200) NOT NULL,
  `last_name` varchar(200) NOT NULL,
  `user_session_key` varchar(32) NOT NULL,
  `user_session_code` int(5) NOT NULL,
  `password` varchar(40) NOT NULL DEFAULT '',
  `email` varchar(80) NOT NULL DEFAULT '',
  `usr_status` enum('Ok','ToActivate') NOT NULL DEFAULT 'ToActivate',
  `msg_notify` enum('yes','no') NOT NULL DEFAULT 'yes',
  `avatar` varchar(225) NOT NULL DEFAULT '',
  `avatar_url` text NOT NULL,
  `sex` enum('male','female') NOT NULL DEFAULT 'male',
  `dob` date NOT NULL DEFAULT '0000-00-00',
  `country` varchar(20) NOT NULL DEFAULT 'PK',
  `level` int(6) NOT NULL DEFAULT '2',
  `avcode` varchar(32) NOT NULL,
  `doj` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `last_logged` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `num_visits` bigint(20) NOT NULL DEFAULT '0',
  `session` varchar(32) NOT NULL DEFAULT '',
  `ip` varchar(15) NOT NULL DEFAULT '',
  `signup_ip` varchar(15) NOT NULL DEFAULT '',
  `time_zone` tinyint(4) NOT NULL DEFAULT '0',
  `featured` enum('No','Yes') NOT NULL DEFAULT 'No',
  `featured_date` datetime NOT NULL,
  `profile_hits` bigint(20) DEFAULT '0',
  `total_watched` bigint(20) NOT NULL DEFAULT '0',
  `total_videos` bigint(20) NOT NULL,
  `total_comments` bigint(20) NOT NULL,
  `total_photos` bigint(255) NOT NULL,
  `total_collections` bigint(255) NOT NULL,
  `comments_count` bigint(20) NOT NULL,
  `last_commented` datetime NOT NULL,
  `voted` text NOT NULL,
  `likes` decimal(5,0) NOT NULL,
  `likes_count` int(6) NOT NULL,
  `ban_status` enum('yes','no') NOT NULL DEFAULT 'no',
  `upload` varchar(20) NOT NULL DEFAULT '1',
  `subscribers` bigint(225) NOT NULL DEFAULT '0',
  `total_subscriptions` bigint(255) NOT NULL,
  `background` mediumtext NOT NULL,
  `background_color` varchar(25) NOT NULL,
  `background_url` text NOT NULL,
  `background_repeat` enum('no-repeat','repeat','repeat-x','repeat-y') NOT NULL DEFAULT 'repeat',
  `background_attachement` enum('yes','no') NOT NULL DEFAULT 'no',
  `total_groups` bigint(20) NOT NULL,
  `last_active` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `banned_users` text NOT NULL,
  `welcome_email_sent` enum('yes','no') NOT NULL DEFAULT 'no',
  `total_downloads` bigint(255) NOT NULL,
  `is_subscribed` enum('yes','no') NOT NULL DEFAULT 'no',
  `album_privacy` enum('public','private','friends') NOT NULL DEFAULT 'private',
  `extras` text NOT NULL,
  `user_filter_level` int(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `cb_users`
  ADD PRIMARY KEY (`userid`),
  ADD KEY `ind_status_doj` (`doj`),
  ADD KEY `ind_status_id` (`userid`),
  ADD KEY `ind_hits_doj` (`profile_hits`,`doj`),
  ADD KEY `username` (`username`);
Run Code Online (Sandbox Code Playgroud)

ype*_*eᵀᴹ 13

这不是我的查询,我正在将在 MySQL 中构建的系统原型转换为 PostgreSQL。此查询在 MySql 中有效。
这是否构成MySql 中错误,或PostgreSQL缺点,或者只是解释差异

更像以上所有;)

这是否构成MySql 中错误

这是许多人认为是错误的“功能”。主要是因为它可能会被误用,就像您(或编写第一个查询的人)误用它一样。

该功能的正确用法是使用“更短”的GROUP BY子句。任何在功能上依赖GROUP BY列的列都可以从GROUP BY列表中省略,并且可以在聚合中SELECTORDER BY不使用聚合时自由使用。

因此,如果对 存在唯一约束subs (subscribed_to, userid),则您的第一个查询是有效的 SQL - 根据某些(2011 年或 2013 年)为 增加的功能GROUP BY)。

您的 DDL 定义没有这样的UNIQUE约束,因此您的开发人员误用了它。它产生正确结果(如果确实如此)的原因很可能是数据符合这样的UNIQUE约束,尽管它没有被强制执行。用户只能订阅一本杂志(或任何subscribed_to涉及的内容)似乎是合理的。

您的数据也可能不符合此类约束,在这种情况下,查询一直为您提供错误的结果 - 您只是没有注意到!检查您的数据和查询的输出(在 MySQL 中)。您是否为同一个(杂志或其他)和同一个用户订阅了许多订阅?在这些情况下,date_added返回的是哪个?并询问业务用户(知道查询要求的人),其中date_added应该返回哪些?最后一个(来自用户和杂志的众多)?第一个?一个随机的?

列表中的其余列(来自users表)SELECT实际上已正确显示,因为两个表之间的连接userid出现在GROUP BY列表中,并且对 有UNIQUE约束users

另请注意,MySQL 并未真正检查所有这些内容(至少在 5.6 版之前)。正确使用该功能是开发人员的责任。并且由于要正确处理它是如此复杂,难怪它被广泛滥用,在许多情况下给出不正确或不一致甚至明显不存在的结果。这就是为什么很多人认为这是一个错误。(该功能在 MySQL 5.7 版本中得到了广泛改进。)

这是否构成了PostgreSQL缺点

并不真地。Postgres 实现了相同的功能,但略有不同——比 MySQL 更受限制。Postgres 仅在GROUP BY列具有PRIMARY KEY一个(或多个)表时使用它。在这种情况下,您可以在没有聚合的情况下自由使用SELECTORDER BY列表中(这些表的)列的其余部分。它不适用于UNIQUE约束,也不适用于来自外键的(可证明的)约束。

这是否构成解释差异

如上所述,是的。两个 DBMS 实现的功能不同,因此对查询的解释也不同。

MySQL(最高5.6)说“我不在乎,我会给你一些结果。如果它们不正确,你要负责)”

Postgres 说:“只有当我 100% 确定它们一致且正确时,我才会给你结果。”


那么,如何做才能使查询正常工作呢?

我们有两个问题:

  • 对于users表格,很简单:添加users.useridGROUP BY列表中。

  • 对于subs表和date_added列,了解用户想要/期望的结果很重要。如果他们想要最早的日期(每个usersubscribed_to),则使用MIN(date_added). 如果他们想要最新的,请使用MAX(date_added).

查询变为 - 并在所有 MySQL 和 Postgres 版本中正常工作:

SELECT MIN(subs.date_added) AS min_date_added, 
       users.userid, users.username,      --- any users column you need
       --- 
       subs.userid AS subscriber 
FROM cb_subscriptions AS subs 
     LEFT JOIN cb_users AS users 
     ON subs.userid = users.userid 
WHERE subs.subscribed_to = '2960' 
GROUP BY users.userid, 
         -- subs.subscribed_to,    -- not needed really, it's fixed in WHERE
         subs.userid 
ORDER BY min_date_added ASC 
LIMIT 8 ;
Run Code Online (Sandbox Code Playgroud)