【问题标题】:SQL - Union count(*) from 3 different tables cause cartesian productSQL - 来自 3 个不同表的联合计数(*)导致笛卡尔积
【发布时间】:2014-06-20 10:15:11
【问题描述】:

我有 3 个表格:帖子、图片和相册。 我需要计算每年发表的帖子、图片和相册的数量。 这里是表的创建:

create table Posts
(
 PostID int primary key identity,
 Description varchar(30) not null,
 Date smalldatetime not null,
 UserWriterID int references Users(UserID)
)

create table Albums
(
 AlbumID int primary key identity,
 Name varchar(20) not null,
 Description varchar(30) not null,
 Date smalldatetime not null,
 AlbumOwnerID int references Users(UserID) 
)

create table Pictures
(
 PictureID int primary key identity,
 AlbumID int references Albums(AlbumID),
 Description varchar(30) not null,
 Date smalldatetime not null,
)

表格中的一些数据样本:

图片:

PictureID | AlbumID | Desc | Date

    1    |   1   |   aa   | 2000-01-01 00:00:00

    2    |   1   |   bb   | 2011-08-31 15:24:00

    10   |   2   |   d    | 2010-07-17 12:35:00

专辑:

AlbumID | Name |    Desc    |    Date     |      AlbumOwnerID

1   |   My album | my new album |  2000-01-01 00:00:00 |    2

2   |   Belgium trip    | trip in Belgium |  2005-07-08 00:00:00 |  2

9   |    Work   | Work and I  | 2011-07-08 00:00:00  |  11

帖子:

PostID  | Desc | Date | UserWriterID

1  |  Feeling good |    2013-09-10 07:44:00 |  2

2   |  FUN FUN FUN |  2015-12-21 09:45:00  |    8

3   |   Whats up?   | 2014-01-18 12:54:00 |     7 

然后我插入数据。

现在,我编写了 3 个查询来获取每个表每年的计数。

create view PostPerYear as
select YEAR(Date) as Year , count(PostID) as Posts
from Posts
group by YEAR(Date)
go 

create view AlbumsPerYear as
select YEAR(Date) as Year , count(AlbumID) as Albums
from Albums 
group by YEAR(Date)
go

create view PicturesPerYear as
select YEAR(Date) as Year , count(PictureID) as Pictures
from Pictures
group by YEAR(Date)
go

但是,我需要一个能显示以上所有结果的查询TOGETHER。 例如:

年份 |帖子数 |专辑数量 |图片计数

2013 | 3 | 4 | 1

2005 | 13 |23 | 5

当我尝试时,我得到了笛卡尔积,例如:

select (p.Year) , (p.Posts) ,  (a.Albums) , (pic.Pictures)
from PostPerYear p ,AlbumsPerYear a ,PicturesPerYear pic
group by p.Year, p.Posts , a.Albums , pic.Pictures

结果是: 年份|帖子 |相册 |图片

2013    9   1   1

2013    9   1   2

2013    9   1   3

2013    9   1   4

2013    9   1   5

2013    9   1   6

2013    9   1   11

2013    9   2   1

2013    9   2   2

2013    9   2   6

2013    9   2   11

2014    10  1   1

2014    10  1   2

2014    10  1   3

2014    10  1   4

2014    10  1   5

2014    10  1   6

And Continued....
------------------------------
------------------------------

请问答案是什么?

谢谢!

【问题讨论】:

  • 附带说明,如果您的表很大,您当前的查询将会因为忽略date 上的任何索引而变慢。如果你有类似日历表的东西,你应该可以把它变成一个范围查询。

标签: mysql sql sql-server count cartesian-product


【解决方案1】:

FROM 子句中指定多个表相当于执行CROSS JOIN。你可以使用UNION ALL来解决这个问题。

WITH summary(year, posts, albums, pictures) AS (        
    SELECT year, posts, 0, 0 FROM PostsPerYear        
    UNION ALL        
    SELECT year, 0, albums, 0 FROM AlbumsPerYear        
    UNION ALL        
    SELECT year, 0, 0, pictures FROM PicturesPerYear        
)        
SELECT year,        
       sum(posts)    AS posts,        
       sum(albums)   AS albums,        
       sum(pictures) AS pictures        
FROM summary        
GROUP BY year;

【讨论】:

  • 它以这样的零返回:2000 0 0 2 2001 0 0 4 2002 0 0 4 2003 0 0 2 2005 0 0 6 2006 0 0 1 2007 0 0 4 2008 0 0 2 2009 0 0 6 2010 0 0 7 2011 0 0 13 2012 0 0 5 2013 0 0 9 2014 0 0 10 2015 0 0 8 2016 0 0 8 2017 0 0 12 2018 0 0 8 2019 0 0 7
  • 您能提供视图中的数据吗?
  • 请看我的fiddle,看看输出是否正确。
  • 有效!你说得对!我复制不正确。谢谢!
【解决方案2】:

首先是简化的 DDL 和一些示例数据:

create table Posts
(
 PostID int primary key identity,
 Date smalldatetime not null
)

create table Albums
(
 AlbumID int primary key identity,
 Date smalldatetime not null
)

create table Pictures
(
 PictureID int primary key identity,
 Date smalldatetime not null,
)




create view PostPerYear as
select YEAR(Date) as Year , count(PostID) as Posts
from Posts
group by YEAR(Date)
go 

create view AlbumsPerYear as
select YEAR(Date) as Year , count(AlbumID) as Albums
from Albums 
group by YEAR(Date)
go

create view PicturesPerYear as
select YEAR(Date) as Year , count(PictureID) as Pictures
from Pictures
group by YEAR(Date)
go


insert posts values('20130101'), ('20140202')
insert albums values('20130101'), ('20140202'), ('20140202')
insert pictures values('20130101'), ('20130101'), ('20140202')
go

现在,使用外连接(我建议使用 ANSI 连接语法):

select coalesce(p.Year, a.year, pic.year) as year, p.Posts, a.Albums, pic.Pictures
from PostPerYear p 
full outer join AlbumsPerYear a on p.year = a.year
full outer join PicturesPerYear pic on p.year = pic.year

返回这个:

Year    Posts   Albums  Pictures
2013    1   1   2
2014    1   2   1

【讨论】:

  • 它返回相同但带有空值。年份 |帖子 |相册 |图片 NULL NULL 1 NULL NULL NULL 2 NULL NULL 1 NULL NULL NULL 2 NULL NULL NULL 1 NULL NULL NULL 1 NULL NULL 1 NULL NULL NULL 2 NULL 2013 9 NULL NULL 2016 8 NULL NULL 2019 7 NULL NULL 2014 10 NULL NULL 2017 12 NULL NULL 2018 8 NULL NULL 2015 8 NULL NULL NULL NULL NULL 6 NULL NULL NULL 2 NULL NULL NULL 3 NULL NULL NULL 3 NULL NULL NULL 2 NULL NULL 4 NULL NULL NULL 1 NULL NULL NULL 2 NULL NULL NULL 11 NULL NULL NULL 1 NULL NULL NULL 5 NULL NULL NULL 5
  • 它没有,我已经在我的答案中包含了一个完整的示例。
  • 对不起,但这是我得到的......每个视图都正确返回。您的查询返回 NULL。这里是:[链接]oi58.tinypic.com/2u70t3a.jpg
  • 应该使用coalesce 年。如果仍然无法正常工作,请给我一些示例数据。
  • 它只返回帖子数。其他为空。唯一出现的年份是 2013 年到 2019 年,这是发帖的年份,而不是专辑和图片的年份
猜你喜欢
  • 2018-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
  • 1970-01-01
  • 1970-01-01
  • 2014-04-08
  • 1970-01-01
相关资源
最近更新 更多