【问题标题】:PostgreSQL select statementPostgreSQL 选择语句
【发布时间】:2012-03-25 11:35:18
【问题描述】:

给定以下表格:

 shows:

      title      | basic_ticket_price
 ----------------+--------------------
  Inception      |              $3.50
  Romeo & Juliet |              $2.00

performance:

  perf_date  | perf_time |     title
 ------------+-----------+----------------
  2012-08-14 | 00:08:00  | Inception
  2012-08-12 | 00:12:00  | Romeo & Juliet

booking:
  ticket_no | perf_date  | perf_time | row_no | person_id
 -----------+------------+-----------+--------+-----------
          1 | 2012-08-14 | 00:08:00  | P01    | 1
          2 | 2012-08-12 | 00:12:00  | O05    | 4
          3 | 2012-08-12 | 00:12:00  | A01    | 2

还有一个额外的表格:seat,其中包含一个座位列表,编号与预订中的 row_no 相同,带有区域名称。

使用此语句对预订的座位进行分组:

select count(row_no) AS row_no,
       area_name 
from seat 
where exists (select row_no 
              from   booking 
              where booking.row_no = seat.row_no) 
group by area_name;

产生:

    row_no |  area_name
   --------+--------------
         1 | rear stalls
         2 | front stalls

我现在如何使用计数的行数和 area_name 编写一条 SQL 语句来生成一个列表,显示节目名称、演出日期和时间以及每个区域的预订座位数?

我试过了:

 select s.title,
        perf_date,
        perf_time,
        count(row_no) AS row_no,
        area_name 
 from shows s, 
      performance,
      seat 
 where exists (select row_no 
               from booking 
               where booking.row_no =  seat.row_no) 
 group by area_name,s.title,performance.perf_date,performance.perf_time;

但它显示重复的行:

      title      | perf_date  | perf_time | row_no |  area_name
 ----------------+------------+-----------+--------+--------------
  Romeo & Juliet | 2012-08-12 | 00:12:00  |      1 | rear stalls
  Romeo & Juliet | 2012-08-14 | 00:08:00  |      2 | front stalls
  Inception      | 2012-08-12 | 00:12:00  |      1 | rear stalls
  Inception      | 2012-08-14 | 00:08:00  |      2 | front stalls
  Inception      | 2012-08-14 | 00:08:00  |      1 | rear stalls
  Inception      | 2012-08-12 | 00:12:00  |      2 | front stalls
  Romeo & Juliet | 2012-08-14 | 00:08:00  |      1 | rear stalls
  Romeo & Juliet | 2012-08-12 | 00:12:00  |      2 | front stalls
  (8 rows)

任何解决此问题的帮助将不胜感激。

【问题讨论】:

  • 另外:您在第二个语句中缺少连接条件。它将在表演、表演和座位之间产生一个笛卡尔连接。你最好用JOIN ...重写它
  • 请接受我的歉意,我正在使用 psql 但也将其标记为 mysql,因为我发现大多数语句与 select 语句相似。
  • 您应该使用序列号作为主键。如果你有第二个放映电影的房间怎么办?你不能让两部电影同时发生,因为它们是由 perf_date 和 perf_time 标识的。或者在安排演出时该怎么办?您必须修改多个表,这是一件坏事。还有一个小建议:我不会在列名中重复表标题。否则以 perf.perf_title 结尾 - perf.title 就足够了。
  • @userunknown: perf_date 实际上是一个可接受的标识符选择。您不想滥用类型名称作为列名。除此之外,它真的应该是timestamp。我会添加一个答案。

标签: sql postgresql


【解决方案1】:

您应该考虑将perf_date dateperf_time time 列合并为一个timestamp 列:

perf_timestamp timestamp

如果您需要来自时间戳的datetime,只需像这样进行转换:

SELECT perf_timestamp::time;
SELECT perf_timestamp::date;

我通常建议使用代理主键。 “节目”(电影标题)的名称不是自然键 - 它不是唯一的。或者,正如@user_unknown 已经提到的那样:开始时间不是表演的实际主键。您可以为此使用serial 列。整个设置可能如下所示:

 -- show:
CREATE TEMP TABLE show (
 show_id serial PRIMARY KEY
,title text
,basic_ticket_price money  -- or numeric
);
INSERT INTO show (title, basic_ticket_price) VALUES
 ('Inception', 3.50)
,('Romeo & Juliet', 2.00);

-- performance:
CREATE TEMP TABLE performance (
 performance_id serial PRIMARY KEY
,show_id int REFERENCES show(show_id) ON UPDATE CASCADE
,perf_start timestamp
);
INSERT INTO performance (show_id, perf_start) VALUES
 (1, '2012-08-14 00:08')
,(2, '2012-08-12 00:12');

-- seat:
CREATE TEMP TABLE seat (
 row_no text PRIMARY KEY
,area_name text
);
INSERT INTO seat (row_no, area_name) VALUES
 ('P01', 'rear stalls')
,('O05', 'front stalls')
,('A01', 'front stalls');

-- booking:
CREATE TEMP TABLE booking (
 ticket_id serial PRIMARY KEY
,performance_id int REFERENCES performance(performance_id) ON UPDATE CASCADE
,row_no text REFERENCES seat(row_no) ON UPDATE CASCADE
,person_id int -- REFERENCES ?
);
INSERT INTO booking (performance_id, row_no, person_id) VALUES
 (1, 'P01', 1)
,(2, 'O05', 4)
,(2, 'A01', 2);

那么您的查询可能如下所示:

SELECT p.perf_start
      ,sh.title
      ,s.area_name
      ,count(*) booked
FROM   booking     b
JOIN   seat        s USING (row_no)
JOIN   performance p USING (performance_id)
JOIN   show        sh USING (show_id)
GROUP  BY 1,2,3
ORDER  BY 1,2,3;

结果:

 perf_start          | title          | area_name    | booked
---------------------+----------------+--------------+-------
 2012-08-12 00:12:00 | Romeo & Juliet | front stalls | 2
 2012-08-14 00:08:00 | Inception      | rear stalls  | 1

【讨论】:

    【解决方案2】:

    在每个表演条目中,查找所有预订。在这些预订中,链接到座位表以确定区域名称。然后,简单的 COUNT(*) 按每个节目/区域的标准分组。

    SELECT
          p.perf_date,
          p.perf_time,
          p.title,
          s.area_name,
          COUNT(*) as SeatsSold
       from
          performance p
    
             JOIN booking b
                ON p.perf_date = b.perf_date
                AND p.perf_time = b.perf_time
    
                JOIN seat s
                   ON b.row_no = s.row_no
       group by
          p.perf_date,
          p.perf_time,
          p.title,
          s.area_name
    

    【讨论】:

      【解决方案3】:

      您的行不会重复。请注意结果中datearea_name 的差异。在我看来一切都很好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-04-14
        • 1970-01-01
        • 2010-10-03
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 2013-06-22
        相关资源
        最近更新 更多