【问题标题】:Find spectators that have seen the same shows (match multiple rows for each)查找看过相同节目的观众(每人匹配多行)
【发布时间】:2015-05-01 13:23:15
【问题描述】:

对于一个作业,我必须为存储在运行 PostgreSQL 9.3.0 的 PostgreSQL 服务器中的数据库编写几个 SQL 查询。但是,我发现自己被最后一个查询阻止了。该数据库模拟了歌剧院的预订系统。该查询是关于将一​​名观众与每次协助相同事件的其他观众相关联。

模型如下所示:

Reservations table 
id_res |     create_date     |  tickets_presented  | id_show | id_spectator | price | category 
-------+---------------------+---------------------+---------+--------------+-------+----------
     1 | 2015-08-05 17:45:03 |                     |       1 |            1 |   195 |        1
     2 | 2014-03-15 14:51:08 | 2014-11-30 14:17:00 |      11 |            1 |   150 |        2

Spectators table

id_spectator   | last_name  | first_name |                email                   |     create_time     | age 
---------------+------------+------------+----------------------------------------+---------------------+-----   
             1 | gonzalez   | colin      | colin.gonzalez@gmail.com               | 2014-03-15 14:21:30 |  22
             2 | bequet     | camille    | bequet.camille@gmail.com               | 2014-12-10 15:22:31 |  22

Shows table
 id_show |          name          |  kind  | presentation_date | start_time | end_time | id_season | capacity_cat1 | capacity_cat2 | capacity_cat3 | price_cat1 | price_cat2 | price_cat3 
---------+------------------------+--------+-------------------+------------+----------+-----------+---------------+---------------+---------------+------------+------------+------------
       1 | madama butterfly       | opera  | 2015-09-05        | 19:30:00   | 21:30:00 |         2 |           315 |           630 |           945 |        195 |        150 |        100
       2 | don giovanni           | opera  | 2015-09-12        | 19:30:00   | 21:45:00 |         2 |           315 |           630 |           945 |        195 |        150 |        100

到目前为止,我已经开始编写一个查询来获取观众的 id 和他正在参加的节目的日期,查询看起来像这样。

SELECT Reservations.id_spectator, Shows.presentation_date
FROM Reservations
LEFT JOIN Shows ON Reservations.id_show = Shows.id_show;

有人可以帮助我更好地理解问题并提示我找到解决方案。提前致谢。

所以我期待的结果应该是这样的

id_spectator | other_id_spectators
-------------+--------------------
            1|                 2,3

意味着每次 id 为 1 的观众去看表演时,观众 2 和 3 也去了。

【问题讨论】:

  • 这里有一个提示——你只需要从一个表中选择。
  • @Hogan 你能不能说得具体一点。
  • 真的吗?你不知道是哪张桌子?只有3个。哪个表有观众和节目的ID?
  • @Hogan,好的,是的,我想我们可以从 Reservations 表中获取 id_spectators 和 id_shows,但我不明白它如何引导我得到最终答案。
  • 我有一个政策不回答标记有多个平台的问题,因为 sql 可能因平台而异还是postgresql?

标签: sql postgresql aggregate-functions common-table-expression relational-division


【解决方案1】:

基于 cmets 的注释:想明确说明此答案可能用途有限,因为它是在 SQL-Server 上下文中回答的(当时存在标签)

可能有更好的方法可以做到这一点,但您可以使用“stuff”功能来做到这一点。这里唯一的缺点是,由于您的 id 是整数,因此在值之间放置逗号将涉及解决方法(需要是字符串)。以下是我能想到的解决方法。

SELECT [id_spectator], [id_show]
, STUFF((SELECT ',' + CAST(A.[id_spectator] as NVARCHAR(10))
FROM reservations A
Where A.[id_show]=B.[id_show] AND a.[id_spectator] != b.[id_spectator] FOR XML PATH('')),1,1,'') As [other_id_spectators]
From reservations B
Group By [id_spectator], [id_show]

这将向您显示参加过相同节目的所有其他观众。

【讨论】:

  • 经过深思熟虑后,不再需要制作临时表。可以只使用 cast 函数,并相应更新。
  • 我想到了,我正在尝试你的提议,似乎行得通。谢谢!
  • 所以我可以使用字符串 agg 函数
  • 这与在 Postgres 中工作的任何东西都不相近。 stuff 在 Postgres 中无效,XML PATH 或非标准方括号而不是标准双引号等也无效。此外,它在 SQL Server 中也不起作用:sqlfiddle.com/#!6/33dc7/1
  • 发布问题时,它有一个 sql 标签,因此以这种方式回答。在暂存数据之后,我在实际的 SQL 中运行了查询并且它起作用了,我没有使用 sql fiddle。您是否尝试过在 sql 中运行它? sqlfiddle 与 MS SQL 是否可能存在差异?
【解决方案2】:

意味着每次 id 为 1 的观众去看表演时,观众 2 和 3 也去了。

换句话说,您需要一个列表...
所有看过给定观众看过的所有节目的观众(可能比给定的更多) p>

这是关系除法的特例。我们在这里汇集了一系列基本技术:

这很特别,因为每个观众必须参加的节目列表是由给定的主要观众动态确定的。

假设(d_spectator, id_show)reservations中是唯一的,这一点没有搞清楚。

对这两列(按此顺序)的UNIQUE 约束也提供了最重要的索引。
为了在下面的查询 2 和 3 中获得最佳性能,还要创建一个带有前导 id_show 的索引。

1。蛮力

原始方法是对给定用户已经看过的节目形成一个排序数组,并比较其他相同的数组:

SELECT 1 AS id_spectator, array_agg(sub.id_spectator) AS id_other_spectators
FROM  (
   SELECT id_spectator
   FROM   reservations r
   WHERE  id_spectator <> 1
   GROUP  BY 1
   HAVING        array_agg(id_show ORDER BY id_show)
      @> (SELECT array_agg(id_show ORDER BY id_show)
          FROM   reservations
          WHERE  id_spectator = 1)
   ) sub;

但这对于大桌子来说可能非常昂贵。整个表必须是进程,而且方式也相当昂贵。

2。更智能

使用CTE 确定相关节目,然后只考虑那些

WITH shows AS (             -- all shows of id 1; 1 row per show
   SELECT id_spectator, id_show
   FROM   reservations
   WHERE  id_spectator = 1  -- your prime spectator here
   )
SELECT sub.id_spectator, array_agg(sub.other) AS id_other_spectators
FROM  (
   SELECT s.id_spectator, r.id_spectator AS other
   FROM   shows s
   JOIN   reservations r USING (id_show)
   WHERE  r.id_spectator <> s.id_spectator
   GROUP  BY 1,2
   HAVING count(*) = (SELECT count(*) FROM shows)
   ) sub
GROUP  BY 1;

@&gt; is the "contains2 operator for arrays - 所以我们让所有至少看过相同节目的观众。

1. 快,因为只考虑相关节目。

3。真正的聪明

若还要从查询中排除不会提前获得资格的观众,请使用recursive CTE

WITH RECURSIVE shows AS (   -- produces exactly 1 row
   SELECT id_spectator, array_agg(id_show) AS shows, count(*) AS ct
   FROM   reservations
   WHERE  id_spectator = 1  -- your prime spectator here
   GROUP  BY 1
   )
, cte AS (
   SELECT r.id_spectator, 1 AS idx
   FROM   shows s
   JOIN   reservations r ON r.id_show = s.shows[1]
   WHERE  r.id_spectator <> s.id_spectator

   UNION  ALL
   SELECT r.id_spectator, idx + 1
   FROM   cte c
   JOIN   reservations r USING (id_spectator)
   JOIN   shows s ON s.shows[c.idx + 1] = r.id_show
   )
SELECT s.id_spectator, array_agg(c.id_spectator) AS id_other_spectators
FROM   shows s
JOIN   cte c ON c.idx = s.ct  -- has an entry for every show
GROUP  BY 1;

请注意,第一个 CTE 是非递归的。只有第二部分是递归的(实际上是迭代的)。

这应该是从大表中选择小部分最快的。不符合条件的行被提前排除。我提到的两个指标是必不可少的。

SQL Fiddle 演示所有三个。

【讨论】:

  • @ErwinBrandsetter 哦,你是对的 (id_spectator, id_show) 在每个预订中都是独一无二的
【解决方案3】:

听起来你有一半的问题 - 确定哪个 id_show 参加了特定的 id_spectator。

您想问自己的是,在给定 id_show 的情况下,如何确定哪些 id_spectators 参加了 id_show。一旦你有了这个,结合这两个答案来得到完整的结果。

【讨论】:

  • 我认为这不是很有帮助,因为这仍然只描述了大约一半的解决方案
【解决方案4】:

所以我得到的最终答案是这样的:

SELECT id_spectator, id_show,(
    SELECT string_agg(to_char(A.id_spectator, '999'), ',')
    FROM Reservations A
    WHERE A.id_show=B.id_show
) AS other_id_spectators
FROM Reservations B
GROUP By id_spectator, id_show
ORDER BY id_spectator ASC;

打印如下内容:

id_spectator | id_show | other_id_spectators 
-------------+---------+---------------------
           1 |       1 |    1,   2,   9
           1 |      14 |    1,   2

这符合我的需要,但是如果您有任何改进,请分享:) 再次感谢大家!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 2018-05-07
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    相关资源
    最近更新 更多