简体   繁体   English

无法找出涉及MySQL中4个表的SQL查询

[英]Unable to figure out a SQL query involving 4 tables in MySQL

There is a query in SQL where I am badly stuck, I have tried every possible way I could but couldn't get the solution. 在SQL中有一个查询,我严重陷入困境,我已经尝试了所有可能的方法,但无法得到解决方案。 I have 4 tables named: user, item, buys, rates. 我有4个表名为:用户,项目,购买,费率。

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')

I have to find against each user all the items not bought by him, but display only those item/items among them which has/have the highest average rating. 我必须针对每个用户找到他未购买的所有物品,但仅显示其中具有/具有最高平均等级的物品/物品。 So the result should display only those item/items which were not bought by him and have the highest average rating. 因此,结果应仅显示那些未被他购买且具有最高平均评级的项目/项目。 Rating is 1-5, and each item may have different ratings so avg rating can be calculated for each, but I am not able to find out items with highest avg rating gaainst each user which are not bought by him. 评分是1-5,并且每个项目可能具有不同的评级,因此可以计算每个评级的平均评级,但是我无法找到每个用户未获得最高平均评级的项目。 I am working in MYSQL, I am stuck in here for 6 days and even my friends tried nobody could solve it. 我在MYSQL工作,我被困在这里6天,甚至我的朋友们都试过没人能解决它。 Can anybody help? 有人可以帮忙吗?

Expected Output considering the current tables should be like this: 考虑当前表格的预期输出应如下:

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

Ok, definitely not my prettiest work, especially since I don't usually work in MySQL (EDIT: SQLFiddle is back up. Fixed an inner group, now this works): 好吧,绝对不是我最漂亮的工作,特别是因为我通常不在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 这是SQLFiddle

In TSQL, I would at least use a CTE for that table of average ratings. 在TSQL中,我至少会使用CTE来获得平均评级表。 This was a lot tougher than it looked initially! 这比最初看起来要困难得多!

EDIT: Some explanation follows. 编辑:一些解释如下。 The first thing to get is each item's average rating, using 0 for items with no rating (hence the COALESCE() statement): 首先要得到的是每个项目的平均评分,对于没有评级的项目使用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)

This will list each item id once with its average rating. 这将使用其平均评级列出每个项目ID一次。 I named this averages , and I actually used it query twice (naming it iwa the second time. I don't remember what "iwa" was supposed to mean any more...), once to get the actual highest rating: 我命名这个averages ,我实际上用它两次查询(第二次命名为iwa 。我不记得“iwa”应该是什么意思了......),一次得到实际的最高评级:

SELECT
  MAX(AverageRating) AS MaxRating
FROM averages

and named that MaxOuterRating , then INNER JOIN ed that result back onto iwa , on AverageRating = MaxRating , to only get item(s) with that highest rating: 并将其命名为MaxOuterRating ,然后INNER JOIN将结果返回到iwa ,在AverageRating = MaxRating ,仅获得具有最高评级的项目:

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

This result is contained in the topItems alias. 此结果包含在topItems别名中。

Now that we have only the item(s) with the top rating, CROSS JOIN with User to get a table with every top item for every user: 现在我们只有最高评级的项目, CROSS JOIN with User可以获得包含每个用户的每个顶级项目的表格:

SELECT 
  ... 
FROM
topItems
CROSS JOIN
Users

This result is in topItemsAllUsers . 此结果位于topItemsAllUsers

Finally, do LEFT JOIN with Buys on both user id and item id, then restrict results to only those rows where there are no Buys records associated (This is usually called an exclude join): 最后,使用Buys对用户ID和项ID进行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

Et viola. 中提琴。 None of the operations is particularly difficult, but they are nested up so badly, it was difficult to see how to attack. 这些操作都不是特别困难,但它们的嵌套非常严重,很难看出如何攻击。 I do not doubt this could be improved greatly, but this does return expected results. 我不怀疑这可以大大改善,但这确实会带来预期的结果。

So just for starters, the list of items not bought by respective users is as follows, right? 所以对于初学者来说,各个用户未购买的商品清单如下,对吧?

    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;

... in which case ... ... 在这种情况下 ...

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;

... should produce the desired result ......应该产生预期的结果

Edited to incorporate Paul Griffin's observation, although in doing so, I've perhaps made the query more convoluted than it needs to be. 编辑融入保罗格里芬的观察,尽管这样做,我可能使查询比它需要的更复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM