【问题标题】:SQL Server JOIN returning multiple rows - need one row on left sideSQL Server JOIN 返回多行 - 左侧需要一行
【发布时间】:2021-12-22 02:43:20
【问题描述】:

我已经在 Google 上进行了广泛的搜索和测试。我想我可能是大脑衰退了。

我们的网站有各种产品的特定登录页面。

Control 表保存页面标题和产品搜索信息。

Control

ID       Title                Category        Colour
------------------------------------------------------------
1        Blue Postcards       Postcard        Blue
2        Blue Photos          Photo           Blue
3        Yellow Postcards     Postcard        Yellow
4        Postcards            Postcard        null

Products

pk_ProdID       Category         Price
-------------------------------------------
100             Postcard         59
200             Photo            42
300             Postcard         33
400             Photo            97
500             Postcard         78
600             Postcard         48

每个产品都有零个或多个可以出售的颜色的记录。

Products_Colours

fk_ProdID  Colour
-------------------------
100        Blue
100        Yellow
200        Blue
300        Yellow
400        Red
500        Blue
500        Yellow
600        Blue

如果Control 的 ID 为 1,则页面应显示蓝色明信片。

以下是想要的结果:

ProdID         Category         Price
-------------------------------------------
100            Postcard         59
500            Postcard         78
600            Postcard         48

我实际上得到的是:

ProdID         Category         Price
-------------------------------------------
100            Postcard         59
100            Postcard         59
500            Postcard         78
500            Postcard         78
600            Postcard         48

SQL

此存储过程为我的 PHP 页面返回两个记录集,以减少对数据库的请求数。

DECLARE @OrderBy varchar(1)
SET @OrderBy = 'D'
DECLARE @Row int
SET @Row = 1
DECLARE @ControlID int
SET @Control = 1

/* get control info for web page */
SELECT 
c.ID,
c.Title

FROM dbo.Control c

WHERE c.ID = @ControlID;

/* get search criteria */
WITH ControlSearch AS 
(
SELECT 
ID,
Category,
Colour
FROM Control WHERE ID = @ControlID
),

/* get products that match search criteria */
ProductSearch AS 
(
SELECT 
DISTINCT p.ProdID,       
p.Category
Count(1) OVER() As Total_Records,
   
   ROW_NUMBER() OVER(
   ORDER BY 
    CASE WHEN @OrderBy = 'D' THEN p.Price END DESC,
    CASE WHEN @OrderBy = 'U' THEN p.Price END ASC
    ) As RowNum       
       
   FROM dbo.Products p
   JOIN ControlSearch l ON l.ID = @ControlID
   LEFT JOIN dbo.Products_Colours co ON p.ProdID = co.ProdID
   WHERE
  
   (p.Category = l.Category)
   
   AND       
  
   (co.Colour = l.Colour OR l.Colour is null)
)   

/* return recordset */
SELECT * FROM ProductSearch WHERE RowNum >= @Row AND RowNum < (@Row + 50)

我向用户显示匹配记录的总数,并输出行号,以便我可以按 50 个产品分页。

为什么这会根据它有多少颜色返回多个产品?

【问题讨论】:

  • “为什么这会根据它有多少颜色返回多个产品?” 因为这就是连接的工作方式。如果您有 1 行与 2 行相关,并且您 JOIN 1 行与 2 行相关,则您将获得 2 行。就像如果你有 2 行与 4 行相关,当你 JOIN 时你会得到 8 行。
  • 为什么将子句 p.Category = l.Categoryco.Colour = l.Colour 放在 WHERE 中,而不是在 JOINs 中放置适当的 ON 子句?例如,拥有JOIN ControlSearch l ON p.Category = l.Category 然后拥有WHERE l.ID = @ControlID 更有意义。
  • 如何将其更改为每个匹配的产品仅返回 1 行?
  • 这取决于您想要哪一个“每个产品 1 行”,为什么?为什么它应该选择蓝色而不是黄色,反之亦然?不要说没关系; SQL Server 没有ARBITRARY 功能。

标签: sql-server join left-join


【解决方案1】:

有点摸不着头脑,但也许你真正想要的是EXISTS

DECLARE @ControlID int
SET @ControlID = 1;

SELECT *
FROM dbo.Products P
WHERE EXISTS (SELECT 1
              FROM dbo.Control C
                   LEFT JOIN dbo.Products_Colours PC ON C.Colour = PC.Colour
              WHERE C.Category = P.Category
                AND PC.fk_ProdID = P.pk_ProdID
                AND (PC.Colour IS NOT NULL OR C.Colour IS NULL)
                AND C.ID = @ControlID);

【讨论】:

    【解决方案2】:

    看起来你需要这样的东西

    SELECT
      p.ProdID,       
      p.Category,
      COUNT(*) OVER () AS Total_Records,
      CASE WHEN @OrderBy = 'U'
        THEN COUNT(*) OVER () - ROW_NUMBER() OVER (ORDER BY p.Price) + 1
        ELSE ROW_NUMBER() OVER (ORDER BY p.Price) END
        AS RowNum   
    FROM dbo.Products P
    JOIN dbo.Control C ON C.ID = @ControlID  -- single-row cross join
    WHERE C.Category = P.Category
      AND (
        C.Colour IS NULL OR EXISTS (SELECT 1
            FROM dbo.Products_Colours PC
            WHERE C.Colour = PC.Colour
              AND PC.fk_ProdID = P.pk_ProdID)
      );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-15
      • 1970-01-01
      • 2011-01-17
      • 1970-01-01
      相关资源
      最近更新 更多