【问题标题】:Always Get 1 record from Table 2 based on some condition始终根据某些条件从表 2 中获取 1 条记录
【发布时间】:2015-06-12 06:30:12
【问题描述】:

我有两张表,Product 和 ProductImages。

Product 表有 2 列:ProductID 和 Name

ProductImages 表有 4 列:ID、ProductID、ImageName 和 Primary(bit)。

Product和ProductImages之间的关系是一对多的,所以一个产品可以有很多张图片,但是对于每个产品只有一个ProductImage是Primary。

我需要编写一个查询来获取所有产品及其主图像。如果产品没有主图像,则应获取 ProductId 的第一条记录。

样品表

| 1  | P1 |
| 2  | P2 |
| 3  | P3 |
| 4  | P4 |

产品图片表示例

| 1   | 1 | P1-1 | 1
| 2   | 1 | P1-2 | 0
| 3   | 1 | P1-3 | 0
| 4   | 1 | P1-4 | 0
| 5   | 2 | P2-1 | 1
| 6   | 2 | P2-2 | 0
| 7   | 3 | P3-1 | 0
| 8   | 3 | P3-2 | 0
| 9   | 4 | P4-1 | 0
| 10  | 4 | P4-2 | 0

输出表

| 1   | 1 | P1-1 | 1
| 5   | 2 | P2-1 | 1
| 7   | 3 | P3-1 | 0
| 9   | 4 | P4-1 | 0

我希望我澄清了我的问题。请询问是否需要进一步说明。

【问题讨论】:

  • 请定义“ProductID 的第一条记录”这是如何确定的?
  • 可以使用 ID 列获取 - 可能是 MIN(PI.ID) 或 MAX(PI.ID)
  • 您可以使用连接来获取带有主图像的产品
  • 您在哪些条件下选择了主图像???
  • 您可以添加示例表数据和想要的结果吗?

标签: sql sql-server sql-server-2008 azure-sql-database


【解决方案1】:

你可以用row_number窗口函数简单地做到这一点:

select * from Products p
join (select *, row_number()  
                 over(partition by ProductID order by ID) rn from ProductImages)pi 
      on p.ProductID = pi.ProductID and pi.rn = 1

我假设主图像 ID 将在非主图像 IDs 之前。

【讨论】:

  • 非常简洁的解决方案。
  • 如果我将查询修改为 over(partition by ProductID order by primary desc, ID) 我认为它消除了假设条件。请评论,
  • order by primary desc 不行,你可以试试order by primary desc, ID。如果有 1 个主节点,它将选择它,否则它将选择最小 ID
  • 所有其他投票的答案在此页面上也是正确的。然而,这个解决方案很简洁,最适合我的项目场景,因此选择了这个作为答案。
  • +1 - 又好又短。 row_number()、rank() 等都是非常强大的函数。只是当没有其他方法时我才真正使用它们,因为它们总是订购数据,我尽量避免在可能的情况下订购数据。只是个人喜好...
【解决方案2】:

这有点“又快又脏”,但我工作:

SELECT  pr.ProductID, pr.Name, prim.ImageName, 1 AS IsPrimary
FROM    @product pr
        INNER JOIN @productimage prim ON pr.ProductID = prim.ProductID
WHERE   prim.[Primary] = 1
UNION ALL
SELECT  pr.ProductID, pr.Name, prim.ImageName, 0 AS IsPrimary
FROM    @product pr
        INNER JOIN
        -- Get any image for this Product MIN, MAX,...what you want
        (
            SELECT  ProductID, MIN(ImageName) AS ImageName
            FROM    @productimage
            WHERE   [Primary] = 0
            GROUP BY ProductID

        ) prim ON pr.ProductID = prim.ProductID
        LEFT JOIN
        --Primary Images:
        (
            SELECT ProductID
            FROM @productimage pri
            WHERE pri.[Primary] = 1
        ) primages ON pr.ProductID = primages.ProductID
WHERE   primages.ProductID IS NULL --there is no primary image

第一个查询是针对所有具有主图像的产品,第二个查询是针对那些没有主图像的产品。

【讨论】:

  • 在第二个查询中,您还可以使用带有“WHERE NOT EXISTS(...)..”的构造来确定哪些产品没有主图像,而不是主图像的派生表
  • 是的,在这种情况下会有很多可行的解决方案:)
【解决方案3】:

连接更少,这看起来很整洁并且可以完成工作

select a.ProductId, ProductName, ImageName, b.ID as ImageID, b.[Primary] , b.[Primary] as IsPrimary
into a
from tProduct a 
inner join tProductImages b on a.ProductID = b.ProductID 
where b.[Primary] = 1

;WITH cte AS
(
   SELECT a.ProductId, ProductName, ImageName, b.ID as ImageID, b.[Primary] as IsPrimary ,
         ROW_NUMBER() OVER (PARTITION BY b.ProductId ORDER BY b.ID) AS rn
   from tProduct a 
    inner join tProductImages b on a.ProductID = b.ProductID 
    where b.[Primary] = 0 and a.ProductID not in (select ProductId from a)
)
SELECT ImageID, ProductId, ProductName, ImageName, IsPrimary
FROM cte WHERE rn = 1
union
select  ImageID, ProductId, ProductName, ImageName, IsPrimary
from a

drop table a

供您参考

编辑:

我只是再次经历它,直到我意识到不需要联合查询,只有下面就足够了

;WITH cte AS
(
    SELECT a.ProductId, ProductName, ImageName, b.ID as ImageID, b.[Primary] as IsPrimary ,
         ROW_NUMBER() OVER (PARTITION BY b.ProductId ORDER BY b.[Primary] desc, b.ID) AS rn
    from tProduct a 
    inner join tProductImages b on a.ProductID = b.ProductID 
)
select * from cte where rn = 1

【讨论】:

  • 这行得通,但我需要在一个巨大的查询中实现这个,其中使用了很多列/函数,我正在研究这个以将它与相同的集成。会及时更新
  • 好的,太好了。让我知道以防万一。
【解决方案4】:

决定使用连接和子查询尝试此解决方案:FIDDLE

SELECT * FROM(
SELECT pi.id as ImageID ,p.id as ProductID,
pi.imagename as ImageName ,pi.primarybit as Primary_
FROM product p
JOIN
(select id, imagename,productid,primarybit
 FROM productimage 
 where primarybit = 1) pi
ON p.id = pi.productid
UNION
SELECT pi.id as ImageID ,p.id as ProductID,
pi.imagename as ImageName ,pi.primarybit as Primary_
FROM product p
JOIN productimage pi
ON p.id = pi.productid
WHERE not p.id in(
  select prod.id
  from product prod
  join productimage prodimg
  on prod.id = prodimg.productid
  where prodimg.primarybit = 1)
AND pi.id IN (
  select min(prodimg.id)
  FROM product prod
  join productimage prodimg
  on prod.id = prodimg.productid
  group by prodimg.productid
)) magictable
ORDER BY ImageID

【讨论】:

    【解决方案5】:

    使用交叉应用的简单连接应该可以做到

    Select p.*, i.* 
    from Product p 
    inner join ProductImage i on p.ProductId = i.ProductId
    cross apply(Select 
                 ProductId, 
                 MinProId = MIN(ProductImageId), 
                 PrimaryProductId = MIN(case when IsPrimary=1 then ProductImageId else null end)  
               from ProductImage i  
               where i.ProductId = p.ProductId 
               group by i.ProductId )s
     where i.ProductImageId= isnull(s.PrimaryProductId, s.MinProId)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-27
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      • 2016-11-09
      相关资源
      最近更新 更多