SQL:从视图中聚合函数选取的行中获取列

SQL: Taking Column From a Row Picked By Aggregate Function in View

我有三个 SQL tables:Release(表示电影的发行),Media(表示那些版本;即对于 Blu-ray/DVD 组合,Media 中将有两行,一张蓝光和一张 DVD,指向 Release) 和 [=21 中的同一行=](定义蓝光、DVD、VHS 等)。 Release/MediaMediaType/Media 是一对多的关系,Media 在两者的 "many" 一侧关系。我有一个 ReleasevRelease 的视图,其中包含聚合函数,例如显示与该版本关联的媒体数量的 COUNT。到目前为止,这是我对该视图的看法:

SELECT          dbo.Release.ReleaseID
               ,dbo.Release.Name
               ,CASE WHEN Release.Compilation = 0 THEN 'No' WHEN Release.Compilation = 1 THEN 'Yes' END AS Compilation
               ,dbo.Release.Owner
               ,CASE WHEN Release.LentOut = 0 THEN 'No' WHEN Release.LentOut = 1 THEN 'Yes' END AS LentOut
               ,COUNT(dbo.Media.ReleaseID) AS NumberOfMedia
               ,MIN(dbo.Media.MediaID) AS FirstMediaID
               ,MIN(dbo.MediaType.Name) AS FirstMediaType
FROM            dbo.MediaType INNER JOIN
                dbo.Media ON dbo.MediaType.MediaTypeID = dbo.Media.MediaTypeID RIGHT OUTER JOIN
                dbo.Release ON dbo.Media.ReleaseID = dbo.Release.ReleaseID
GROUP BY        dbo.Release.ReleaseID, dbo.Release.Name, dbo.Release.Compilation, dbo.Release.Owner, dbo.Release.LentOut

您会注意到我还包含了另外两个聚合列:FirstMediaID 获取与 Media table 中最先出现的版本关联的媒体 ID (即,如果一个发行版有两张与之相关联的 DVD,它会获得一张 ID 值较低的 DVD)。这个专栏本身没有用;然后,我想做的是依次获取与 Media 相关联的 MediaType。换句话说,我想要一个显示第一个 MediaMediaType 的列,它附加到每个 Release。之后的列 FirstMediaType 应该这样做,但它却在与 Release 关联的所有 Media 中获取 MediaType 并选择一个按字母顺序排列 - 这意味着 Blu-ray 将始终优先于 DVD(这很好),但音频 CD 将始终优先于其他所有内容(这不好)。

如何获取此视图中的 FirstMediaType 列以获取 FirstMediaID 中标识的媒体的 MediaType

更新:这里是 tables,它们的列和一些示例行。

来自Release的一对:

+-----------+----------------------------------------+-------+-------------+---------+
| ReleaseID |                  Name                  | Owner | Compilation | LentOut |
+-----------+----------------------------------------+-------+-------------+---------+
|         2 | Alice in Wonderland                    | NULL  |           0 |       0 |
|         6 | 4 Film Favorites - Family Comedies     | NULL  |           1 |       0 |
|         8 | Aladdin                                | NULL  |           0 |       0 |
|       463 | Harry Potter and the Half-Blood Prince | NULL  |           0 |       1 |
|       534 | Spirited Away                          | Ryan  |           0 |       0 |
|       571 | The Original Christmas Classics        | NULL  |           1 |       0 |
+-----------+----------------------------------------+-------+-------------+---------+

Compilation 表示发行版中包含不止一部电影。

Media中对应条目:

+---------+-------------+-------------------------------------------------------------------------------------+-----------+
| MediaID | MediaTypeID |                                        Name                                         | ReleaseID |
+---------+-------------+-------------------------------------------------------------------------------------+-----------+
|       2 |           2 | Movie                                                                               |         2 |
|       3 |           1 | Movie                                                                               |         2 |
|      12 |           1 | Space Jam; Looney Tunes: Back in Action                                             |         6 |
|      13 |           1 | Funky Monkey; Osmosis Jones                                                         |         6 |
|      17 |           3 | Movie                                                                               |         8 |
|     620 |           1 | Movie                                                                               |       463 |
|     726 |           1 | Movie                                                                               |       534 |
|     807 |           1 | Rudolph the Red-Nosed Reindeer; Cricket on the Hearth                               |       571 |
|     808 |           1 | Frosty the Snowman; Frosty Returns                                                  |       571 |
|     809 |           1 | Santa Claus is Comin' to Town!; Mr. Magoo's Christmas Carol; The Little Drummer Boy |       571 |
|     810 |           4 | Tracks 1-7                                                                          |       571 |
+---------+-------------+-------------------------------------------------------------------------------------+-----------+

MediaType中的前几名:

+-------------+--------------+
| MediaTypeID |     Name     |
+-------------+--------------+
|           1 | DVD Disc     |
|           2 | Blu-ray Disc |
|           3 | VHS          |
|           4 | Audio CD     |
+-------------+--------------+

vRelease中对应的条目应该是这样的:

+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+
| ReleaseID |                  Name                  | Compilation | Owner | LentOut | NumberOfMedia | FirstMediaID | FirstMediaType |
+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+
|         2 | Alice in Wonderland                    | No          | NULL  | No      |             2 |            2 | Blu-ray Disc   |
|         6 | 4 Film Favorites - Family Comedies     | Yes         | NULL  | No      |             2 |           12 | DVD Disc       |
|         8 | Aladdin                                | No          | NULL  | No      |             1 |           17 | VHS            |
|       463 | Harry Potter and the Half-Blood Prince | No          | NULL  | Yes     |             1 |          620 | DVD Disc       |
|       534 | Spirited Away                          | No          | Ryan  | No      |             1 |          726 | DVD Disc       |
|       571 | The Original Christmas Classics        | Yes         | NULL  | No      |             4 |          807 | DVD Disc       |
+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+

但实际上是这样的:

+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+
| ReleaseID |                  Name                  | Compilation | Owner | LentOut | NumberOfMedia | FirstMediaID | FirstMediaType |
+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+
|         2 | Alice in Wonderland                    | No          | NULL  | No      |             2 |            2 | Blu-ray Disc   |
|         6 | 4 Film Favorites - Family Comedies     | Yes         | NULL  | No      |             2 |           12 | DVD Disc       |
|         8 | Aladdin                                | No          | NULL  | No      |             1 |           17 | VHS            |
|       463 | Harry Potter and the Half-Blood Prince | No          | NULL  | Yes     |             1 |          620 | DVD Disc       |
|       534 | Spirited Away                          | No          | Ryan  | No      |             1 |          726 | DVD Disc       |
|       571 | The Original Christmas Classics        | Yes         | NULL  | No      |             4 |          807 | Audio CD       |
+-----------+----------------------------------------+-------------+-------+---------+---------------+--------------+----------------+

问题出在最后一个。

最简单的方法是在 FirstMediaId = MediaType.MediaId

上向您的 MediaType table 添加另一个连接
;WITH data AS (
    SELECT     dbo.Release.ReleaseID
              ,dbo.Release.Name
              ,CASE WHEN Release.Compilation = 0 THEN 'No' WHEN Release.Compilation = 1 THEN 'Yes' END AS Compilation
              ,dbo.Release.Owner
              ,CASE WHEN Release.LentOut = 0 THEN 'No' WHEN Release.LentOut = 1 THEN 'Yes' END AS LentOut
              ,COUNT(dbo.Media.ReleaseID) AS NumberOfMedia
              ,MIN(dbo.Media.MediaID) AS FirstMediaID
    FROM  dbo.MediaType 
            INNER JOIN dbo.Media 
                ON  dbo.MediaType.MediaTypeID = dbo.Media.MediaTypeID 
            RIGHT OUTER JOIN dbo.Release 
                ON dbo.Media.ReleaseID = dbo.Release.ReleaseID
    GROUP BY dbo.Release.ReleaseID, dbo.Release.Name, dbo.Release.Compilation, dbo.Release.Owner, dbo.Release.LentOut
)
SELECT data.ReleaseId
      ,data.Name
      ,data.Compilation
      ,data.Owner
      ,data.LentOut
      ,data.NumberOfMedia
      ,data.FirstMediaId
      ,MediaType.Name   as FirstMediaName 
FROM data
        LEFT OUTER JOIN dbo.MediaType
            ON  data.FirstMediaId = MediaType.MediaTypeId

returns整行与"First"、"Last"、"Earliest"、"Latest"等需求相关联的一个非常方便的技术是使用row_number() over()。这里你想要"first media type",所以它在这里是相关的。

正如您将在以下查询中看到的那样,加入 [Media] table 被替换为包含行号计算的子查询。这里我们partition byReleaseIDorder byMediaID,所以,对于each ReleaseID 第一行将是具有最低 MediaID 值的行。然后在这个派生的 table 的连接中添加一个额外的条件以仅考虑行号为 1.

的行

建议查询

SELECT
      r.ReleaseID
    , m.MediaID
    , mt.MediaTypeID
    , mt.name MediaName
    , r.Name
    , CASE
            WHEN r.Compilation = 0 THEN 'No'
            WHEN r.Compilation = 1 THEN 'Yes'
      END                        AS compilation
    , r.Owner
    , CASE
            WHEN r.LentOut = 0 THEN 'No'
            WHEN r.LentOut = 1 THEN 'Yes'
      END                        AS lentout
FROM dbo.Release r 
INNER JOIN (
        SELECT
               Media.*
             , ROW_NUMBER() OVER(PARTITION BY ReleaseID
                                 ORDER BY MediaID) AS rn
        FROM dbo.Media 
        ) m ON  r.ReleaseID = m.ReleaseID and rn = 1
INNER JOIN dbo.MediaType mt ON  m.MediaTypeID = mt.MediaTypeID

结果

| ReleaseID | MediaID | MediaTypeID |  MediaName   |                  Name                  | compilation | Owner  | lentout |
|-----------|---------|-------------|--------------|----------------------------------------|-------------|--------|---------|
|         2 |       2 |           2 | Blu-ray Disc | Alice in Wonderland                    | No          | (null) | No      |
|         6 |      12 |           1 | DVD Disc     | 4 Film Favorites - Family Comedies     | Yes         | (null) | No      |
|         8 |      17 |           3 | VHS          | Aladdin                                | No          | (null) | No      |
|       463 |     620 |           1 | DVD Disc     | Harry Potter and the Half-Blood Prince | No          | (null) | Yes     |
|       534 |     726 |           1 | DVD Disc     | Spirited Away                          | No          | Ryan   | No      |
|       571 |     807 |           1 | DVD Disc     | The Original Christmas Classics        | Yes         | (null) | No      |

Demo available at SQLFiddle

我最终找到了一种简单的方法来做我想做的事。它不像 Used_By_Already 的答案那么花哨(据我所知,它确实有效)并且可能在某处打破了 SQL 最佳实践规则,但它更容易理解和保持——至少对我的新手来说是这样。

由于问题是试图让视图使用它在连接中计算的聚合列,我只是将两步操作拆分为两个视图。 vReleasePre 包含我在原始查询中概述的所有列,FirstMediaType 除外。 vRelease 现在只需从 vReleasePre 中获取所有列并添加 FirstMediaType,它从末尾的连接中获取其值:LEFT OUTER JOIN dbo.vMedia ON dbo.vReleasePre.FirstMediaID = dbo.vMedia.MediaID,其中 vMedia 是包含 Media 中所有列的视图,加上 MediaType 列(我已经有 vMedia 了)。

由于此数据库通过 Entity Framework 在 ASP.NET MVC 网络应用程序中使用,并且 EF 对数据模型将接受和不接受的内容一直很奇怪,我想一个简单但迂回的解决方案可能是我的最佳选择。

vReleasePre:

SELECT    dbo.Release.ReleaseID
         ,dbo.Release.Name
         ,CASE WHEN Release.Compilation = 0 THEN 'No' WHEN Release.Compilation = 1 THEN 'Yes' END AS Compilation
         ,dbo.Release.Owner
         ,CASE WHEN Release.LentOut = 0 THEN 'No' WHEN Release.LentOut = 1 THEN 'Yes' END AS LentOut
         ,COUNT(dbo.Media.ReleaseID) AS NumberOfMedia
         ,MIN(dbo.Media.MediaID) AS FirstMediaID
FROM      dbo.MediaType INNER JOIN
          dbo.Media ON dbo.MediaType.MediaTypeID = dbo.Media.MediaTypeID RIGHT OUTER JOIN
          dbo.Release ON dbo.Media.ReleaseID = dbo.Release.ReleaseID
GROUP BY  dbo.Release.ReleaseID, dbo.Release.Name, dbo.Release.Compilation, dbo.Release.Owner, dbo.Release.LentOut

vRelease:

SELECT   dbo.vReleasePre.ReleaseID
        ,dbo.vReleasePre.Name
        ,dbo.vReleasePre.Compilation
        ,dbo.vReleasePre.Owner
        ,dbo.vReleasePre.LentOut
        ,dbo.vReleasePre.NumberOfMedia
        ,dbo.vMedia.MediaType
FROM     dbo.vReleasePre LEFT OUTER JOIN
         dbo.vMedia ON dbo.vReleasePre.FirstMediaID = dbo.vMedia.MediaID

对于新手来说,这是我使用的子查询

    SELECT
           ROW_NUMBER() OVER(PARTITION BY ReleaseID
                             ORDER BY MediaID) AS rn
         , Media.*
    FROM dbo.Media 

这就是它的作用(参见 rn 列)

| rn | MediaID | MediaTypeID |                                        Name                                         | ReleaseID |
|----|---------|-------------|-------------------------------------------------------------------------------------|-----------|
|  1 |       2 |           2 | Movie                                                                               |         2 |
|  2 |       3 |           1 | Movie                                                                               |         2 |
|  1 |      12 |           1 | Space Jam; Looney Tunes: Back in Action                                             |         6 |
|  2 |      13 |           1 | Funky Monkey; Osmosis Jones                                                         |         6 |
|  1 |      17 |           3 | Movie                                                                               |         8 |
|  1 |     620 |           1 | Movie                                                                               |       463 |
|  1 |     726 |           1 | Movie                                                                               |       534 |
|  1 |     807 |           1 | Rudolph the Red-Nosed Reindeer; Cricket on the Hearth                               |       571 |
|  2 |     808 |           1 | Frosty the Snowman; Frosty Returns                                                  |       571 |
|  3 |     809 |           1 | Santa Claus is Comin' to Town!; Mr. Magoo's Christmas Carol; The Little Drummer Boy |       571 |
|  4 |     810 |           4 | Tracks 1-7                                                                          |       571 |

现在只保留 rn 列中有 1 的那些行:

| rn | MediaID | MediaTypeID |                         Name                          | ReleaseID |
|----|---------|-------------|-------------------------------------------------------|-----------|
|  1 |       2 |           2 | Movie                                                 |         2 |
|  1 |      12 |           1 | Space Jam; Looney Tunes: Back in Action               |         6 |
|  1 |      17 |           3 | Movie                                                 |         8 |
|  1 |     620 |           1 | Movie                                                 |       463 |
|  1 |     726 |           1 | Movie                                                 |       534 |
|  1 |     807 |           1 | Rudolph the Red-Nosed Reindeer; Cricket on the Hearth |       571 |

然后加入 那些行 到 Releases 和 MediaType

宾果

=想要的结果。

不难,真的不难。您真的会想要了解这些 window 功能,因为它们可以解决大量问题。