web-dev-qa-db-ja.com

エラー:列「フィールド」はGROUP BY句に出現するか、集計関数で使用する必要があります

私はSQLの問題にぶつかりました。この問題に関して2つの異なる質問があります。

  • どうして?
  • どうすれば回避できますか?

次のクエリ:

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;

...次のエラーを生成します:

ERROR: column "users.userid" must appear in the GROUP BY clause or be used in an aggregate function

そのフィールドはCHARなので、どの種類の集計を使用するのか、または(私の理解ではより重要なのですが)なぜ必要なのかわかりません。

言ったように、なぜこれが起こるのか私にはわかりません。理解できないと思いますgroup by私がやったと思ったのと同様に;)

これは私のクエリではなく、MySQLでプロトタイプ化されたシステムをPostgreSQLに変換しています。このクエリdoesは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;

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

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`);
2
Imran

これは私のクエリではなく、MySQLでプロトタイプ化されたシステムをPostgreSQLに変換しています。このクエリはMySqlでは機能します。
それはMySqlのバグ、またはPostgreSQLの欠点、または単なる解釈の違いを構成しますか?

上記のすべてのように;)

MySqlのバグを構成しますか?

多くの人がバグと考えるのは「機能」です。主にそれが悪用される可能性があるため、あなた(または最初のクエリを書いた人)が悪用したように。

この機能の正しい使い方は、「より短い」_GROUP BY_句を使用することです。 _GROUP BY_列で機能的に依存しているの列は、_GROUP BY_リストから省略でき、集計なしでSELECTおよび_ORDER BY_で自由に使用できます。 。

したがって、subs (subscribed_to, userid)に一意の制約がある場合、最初のクエリは有効なSQLです-_GROUP BY_のいくつかの(2011または2013)追加機能によると)。

DDL定義には、このようなUNIQUE制約がないため、開発者による誤用です。正しい結果が生成される理由(生成された場合)は、強制されていませんが、データがそのようなUNIQUE制約に準拠している可能性があります。ユーザーが雑誌(または_subscribed_to_が参照するもの)へのサブスクリプションを1つだけ持つことができるのはもっともらしいようです。

データがそのような制約に準拠していない可能性もあります。その場合、クエリが間違った結果を出している-気づかなかっただけです!データとクエリの出力を確認します(MySQL)。同じ(雑誌または何でも)および同じユーザーのための多くのサブスクリプションがありますか?その場合、どの_date_added_が返されますか?そして、ビジネスユーザー(クエリの要件を知っている人)に、どの_date_added_を返す必要があるか尋ねます。最後の1つ(ユーザーと雑誌からの多く)ですか?最初の1つ?ランダムな?

usersリストの残りの列(SELECTテーブルから)は、2つのテーブル間の結合がuseridにあり、_GROUP BY_リストとUNIQUEに対するusers制約があります。

MySQLはこれらすべてを実際にチェックするわけではないことにも注意してください(少なくともバージョン5.6まで)。この機能を正しく使用するのは開発者の責任です。また、正しく理解するのは非常に複雑なので、広く誤用され、多くの場合、不正確な、または一貫性のない、あるいは明らかに存在しない結果をもたらすことは不思議ではありません。それが多くの人がそれをバグだと思った理由です。 (この機能はMySQLのバージョン5.7で大幅に改善されました。)

PostgreSQLの欠点を構成しますか?.

あんまり。 Postgresも同じ機能を実装していますが、MySQLよりも制限が少し異なります。 Postgresは、_GROUP BY_列に1つ(または複数)のテーブルの_PRIMARY KEY_がある場合にのみ使用します。その場合は、SELECTおよび_ORDER BY_リストの(それらのテーブルの)残りの列を集計なしで自由に使用できます。 UNIQUE制約では機能せず、外部キーからの(証明可能な)制約では機能しません。

解釈の違いですか?

上記で説明したように、はい。 2つのDBMSでは機能が異なるため、クエリの解釈は異なります。

MySQL(5.6まで)は「気にしません、結果をいくつかあげます。正しくなければ責任があります)」

Postgresは「一貫性があり正しいことを100%確信している場合にのみ結果を提供します。」


では、クエリが正しく機能するようにするにはどうすればよいですか?

2つの問題があります。

  • usersテーブルの場合は簡単です。_users.userid_リストに_GROUP BY_を追加します。

  • subsテーブルと_date_added_列については、ユーザーが望む/期待する結果を知ることが重要です。 (userおよび_subscribed_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 ;
_
13
ypercubeᵀᴹ