【问题标题】:Unable to figure out a SQL query involving 4 tables in MySQL无法找出涉及 MySQL 中 4 个表的 SQL 查询
【发布时间】:2015-05-12 09:36:58
【问题描述】:

在 SQL 中有一个查询,我被严重卡住了,我已经尝试了所有可能的方法,但无法获得解决方案。我有 4 个表,分别命名为:user、item、buys、rates。

CREATE TABLE User (
    id integer,
    name varchar(30),
    Primary Key (id)
)

INSERT INTO User(id, name)
VALUES
('1', 'Lorren'),
('2', 'Smith'),
('3', 'Stephen'),
('4', 'David'),
('5', 'Sophie'),
('6', 'Alex'),
('7', 'Henry'),
('8', 'Jasmine'),
('9', 'Anderson'),
('10', 'Bilal')

CREATE TABLE Item (
    id integer,
    description varchar(50),
    category varchar(30),
    price integer,
    Primary Key (id)
)

INSERT INTO Item(id, description, category, price)
VALUES
('50', 'Princess Diary', 'Book', '8'),
('51', 'Frozen', 'Book', '4'),
('52', 'Tangled', 'Book', '3'),
('53', 'Oak Table', 'Furniture', '370'),
('54', 'Doble Bed', 'Furniture', '450'),
('55', 'Metal Cupboard', 'Furniture', '700'),
('56', 'Levi 501', 'Clothes', '90'),
('57', 'Corduroy Coat', 'Clothes', '230'),
('58', 'Straight Trousers', 'Clothes', '45'),
('59', 'Black Sequin Top', 'Clothes', '85')


CREATE TABLE Buys (
    user integer,
    item integer,
    price integer,
    Primary Key (user, item),
    Foreign key (user) REFERENCES User(id),
    Foreign Key (item) REFERENCES Item(id)
)

INSERT INTO Buys
VALUES ('1', '52', '3'),
('1', '56', '90'),
('2','56','100'),
('2', '54', '450'),
('5', '53', '400'),
('5', '55', '700'),
('5', '59', '90'),
('6', '57', '230'),
('10', '58', '50'),
('8', '50', '8')


CREATE TABLE Rates (
    user integer,
    item integer,
    rating integer CHECK (0<=rating<=5),
    Primary Key (user, item),
    Foreign key (user) REFERENCES User(id),
    Foreign Key (item) REFERENCES Item(id)
)

INSERT INTO Rates
VALUES
('1', '52', '5'),
('1', '56', '3'),
('2', '54', '5'),
('2', '55', '4'),
('2', '56', '2'),
('5', '53', '5'),
('5', '55', '5'),
('8', '50', '1'),
('8', '55', '3'),
('9', '55', '4')

我必须针对每个用户查找他未购买的所有商品,但仅显示其中具有/具有最高平均评分的那些商品/商品。所以结果应该只显示那些不是他买的并且具有最高平均评分的项目。评分为 1-5,每个项目可能有不同的评分,因此可以计算每个项目的平均评分,但我无法找出每个用户没有购买的平均评分最高的项目。我在 MYSQL 中工作,我在这里被困了 6 天,甚至我的朋友都试过没有人能解决它。有人可以帮忙吗?

考虑到当前表的预期输出应该是这样的:

User    Items With Highest Average      
Lorren     53
Lorren     54
Smith      52
Smith      53
Stephen    52
Stephen    53
Stephen    54
David      52
David      53
David      54
Sophie     52
Sophie     54
Alex       52
Alex       53
Alex       54
Henry      52
Henry      53
Henry      54
Jasmine    52
Jasmine    53
Jasmine    54
Anderson   52
Anderson   53
Anderson   54
Bilal      52
Bilal      53
Bilal      54

【问题讨论】:

  • 你能添加预期的结果吗?
  • 你为什么不尝试一些你认为最接近的尝试。没有它,它确实有点像家庭作业交接......
  • 我猜你忘记了 [Ratings] 表中的 [Rating] 列
  • @jean 类似的家庭作业,我已经完成了剩下的十几个查询,只有一个没有解决,它不像你写的那么简单,你读过吗?我们必须为每个查询找到项目用户,不是他买的,平均评分最高,所以会使用聚合 avg() 但我怎么想不通
  • 您了解并熟悉子选择,或者在 MySQL 术语中,子查询?他们可能会帮助你去这里。 dev.mysql.com/doc/refman/5.0/en/subqueries.html

标签: mysql sql database wordpress


【解决方案1】:

好吧,这绝对不是我最漂亮的工作,尤其是因为我通常不使用 MySQL(编辑:SQLFiddle 已备份。修复了一个内部组,现在可以使用):

SELECT topItemsAllUsers.* FROM
  (SELECT 
    u.id AS userId, 
    u.name, 
    topItems.itemId
  FROM
    (SELECT 
      iwa.id AS itemId
    FROM
      (SELECT 
        MAX(AverageRating) AS MaxRating
      FROM
        (SELECT 
          i.id, 
          AVG(COALESCE(r.rating, 0)) AS AverageRating
        FROM Item i
        LEFT JOIN Rates r ON r.item = i.id
        GROUP BY i.id
        ) AS averages
      ) AS MaxOuterRating
      INNER JOIN 
      (SELECT 
        i.id, 
        AVG(COALESCE(r.rating, 0)) AS AverageRating
      FROM Item i
      LEFT JOIN Rates r ON r.item = i.id
      GROUP BY i.id
      ) as iwa ON iwa.AverageRating = MaxOuterRating.MaxRating
    ) as topItems
  CROSS JOIN
  User u
  ) as topItemsAllUsers
LEFT JOIN Buys b ON topItemsAllUsers.userId = b.user AND topItemsAllUsers.itemId = b.item
WHERE b.user IS NULL

Here is the SQLFiddle

在 TSQL 中,我至少会为该平均评级表使用 CTE。这比最初看起来要困难得多!

编辑:一些解释如下。首先得到的是每个项目的平均评分,没有评分的项目使用 0(因此COALESCE() 声明):

(SELECT 
  i.id, 
  AVG(COALESCE(r.rating, 0)) AS AverageRating
FROM Item i
LEFT JOIN Rates r ON r.item = i.id
GROUP BY i.id)

这将列出每个项目 ID 及其平均评分一次。我将其命名为averages,实际上我使用了两次查询(第二次将其命名为iwa。我不记得“iwa”应该是什么意思了……),一次是为了得到实际的最高值评分:

SELECT
  MAX(AverageRating) AS MaxRating
FROM averages

并命名为MaxOuterRating,然后INNER JOINed 将该结果返回到iwa,在AverageRating = MaxRating 上,只获得具有最高评分的项目:

SELECT
  iwa.itemId
FROM
MaxOuterRating
INNER JOIN iwa ON iwa.AverageRating = MaxOuterRating.MaxRating

此结果包含在 topItems 别名中。

现在我们只有评分最高的项目,CROSS JOINUser 得到一个表格,其中包含每个用户的每个最高项目:

SELECT 
  ... 
FROM
topItems
CROSS JOIN
Users

此结果位于topItemsAllUsers

最后,在用户 ID 和项目 ID 上使用 Buys 执行 LEFT JOIN,然后将结果限制为仅那些没有关联 Buys 记录的行(这通常称为排除连接):

SELECT 
  topItemsAllUsers.*
FROM
topItemsAllUsers
LEFT JOIN Buys b ON topItemsAllUsers.userId = b.user AND topItemsAllUsers.itemId = b.item
WHERE b.user IS NULL

等中提琴。没有一个操作是特别困难的,但是它们嵌套得太厉害了,很难看出如何攻击。我不怀疑这可以大大改善,但是这确实返回了预期的结果。

【讨论】:

  • 看到您的解决方案我很激动,您已经做到了,先生。如果您能向我解释一下您在其中做了什么,我将不胜感激
  • 太棒了!你已经解释了它让我更容易。毫无疑问,由于嵌套很复杂,您的大脑会花费很多时间。非常感谢您抽出宝贵的时间来询问我的问题,出色的工作
【解决方案2】:

所以对于初学者来说,各个用户没有购买的物品清单如下,对吧?

    SELECT u.*
         , i.* 
      FROM user u 
      JOIN item i 
      LEFT 
      JOIN buys b 
        ON b.user = u.id 
       AND b.item = i.id 
     WHERE b.item IS NULL;

...在这种情况下...

SELECT x.* FROM
(
SELECT u.id user_id
     , u.name
     , i.id item_id
     , i.description
     , i.category
     , i.price
     , r.rating
  FROM user u 
  JOIN item i 
  LEFT 
  JOIN buys b 
    ON b.user = u.id AND b.item = i.id
  JOIN rates r
    ON r.item = i.id
 WHERE b.item IS NULL
) x
JOIN
(
SELECT u.id,r.rating 
  FROM user u 
  JOIN item i 
  LEFT 
  JOIN buys b 
    ON b.user = u.id AND b.item = i.id
  JOIN rates r
    ON r.item = i.id
  JOIN (SELECT AVG(rating) max_avg FROM rates GROUP BY item ORDER BY AVG(rating) DESC LIMIT 1) n
    ON n.max_avg = r.rating
 WHERE b.item IS NULL
 GROUP 
    BY u.id
 ) y
 ON y.id = x.user_id
AND y.rating = x.rating
ORDER 
  BY user_id,item_id;

...应该会产生预期的结果

经过编辑以纳入 Paul Griffin 的观察,尽管这样做,我可能使查询变得比它需要的更复杂。

【讨论】:

  • 这将获得至少一个评分 == 表中最高评分的项目,但不限于平均评分 == 最高平均评分的项目。
  • @PaulGriffin 你做到了,不是吗?我正在查看您的解决方案,直到现在无法准确掌握您在此解决方案中做了什么。结果是准确的,我想知道它是否适用于表中具有不同评级项目的任意数量的行?我想应该是的。先生,你做到了
  • @strawberry 嘿,感谢您调整解决方案并尝试解决我的问题。你比我预期的更有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多