SQL - 在通过连接点 table 加入 table 时如何避免重复行?

SQL - How to avoid a repeating row when joining tables via a junction table?

我正在构建一个 Pokemon API,并且在通过连接点 table 加入时遇到重复行的问题。它是一个 SQL 服务器数据库。我的 tables 的简化版本如下所示:

重新创建我的数据库:

CREATE DATABASE PokeApi;
USE PokeApi;

CREATE TABLE Pokemon (
    PokemonId INT PRIMARY KEY IDENTITY,
    PokemonName VARCHAR(55),
    NationalDexNumber INT,
    PokemonHeight INT,
    PokemonWeight DECIMAL(10,2),
    Category VARCHAR(50),
    Gender VARCHAR(15),
    GenderDifference BIT,
    TypeOne VARCHAR(15),
    TypeTwo VARCHAR(15),
);

CREATE TABLE Abilities (
    AbilityId INT PRIMARY KEY IDENTITY,
    AbilityName VARCHAR(20)
);

CREATE TABLE PokemonAbilitiesMapped (
    PokemonId INT NOT NULL,
    AbilityId INT,
    CONSTRAINT PK_PokemonAbilitiesMapped PRIMARY KEY
    (
        PokemonId,
        AbilityId
    ),
    FOREIGN KEY(PokemonId) REFERENCES Pokemon(PokemonId),
    FOREIGN KEY(AbilityId) REFERENCES Abilities(AbilityId)
);

INSERT INTO Pokemon
VALUES
  ('Venusaur', 3, 79, 220.5, 'Seed', 'Male / Female', 1, 'Grass', 'Poison'),
  ('Mega Venusaur', 3, 94, 342.8, 'Seed', 'Male / Female', 0, 'Grass', 'Poison'),
  ('Gigantamax Venusaur', 3, 945, NULL, 'Seed', 'Male / Female', 1, 'Grass', 'Poison');

INSERT INTO Abilities
VALUES
  ('Overgrow'),
  ('Thick Fat'),
  ('Test');

INSERT INTO PokemonAbilitiesMapped
VALUES
  (1, 1),
  (2, 2),
  (3, 1),
  (3, 3);

我的查询:

SELECT p.PokemonName, p.NationalDexNumber, p.PokemonHeight, p.PokemonWeight,
p.Category, p.Gender, p.GenderDifference, p.TypeOne, p.TypeTwo,
a.AbilityName
FROM Pokemon p
JOIN PokemonAbilitiesMapped pam
ON p.PokemonId = pam.PokemonId
JOIN Abilities a
ON pam.AbilityId = a.AbilityId

输出这个:

PokemonName NationalDexNumber PokemonHeight PokemonWeight Category Gender GenderDifference TypeOne TypeTwo AbilityName
Venusaur 3 79 220.50 Seed Male / Female True Grass Poison Overgrow
Mega Venusaur 3 94 342.80 Seed Male / Female False Grass Poison Thick Fat
Gigantamax Venusaur 3 945 null Seed Male / Female True Grass Poison Overgrow
Gigantamax Venusaur 3 945 null Seed Male / Female True Grass Poison Test

如您所见,Gigantamax Venusaur 列出了两次,因为它有两个能力。我在想我可以添加另一个名为“AbilityNameTwo”的列,前提是查询的神奇宝贝具有避免重复行的第二种能力。

在这种情况下避免重复数据的最佳方法是什么?如果我应该在加入时添加另一列,我将如何将其写为查询?

连接数据时出现这样的结果是完全正常的。如果你真的想要,你可以拥有“二能力”,但是如果某些神奇宝贝有三能力,或者五能力呢?你如何订购它们?这是一个示例,说明您如何为两种能力执行此操作并按字母顺序排列:

WITH abilitiesMapped AS
(
    SELECT pam.PokemonId
         , a.AbilityName
         , ROW_NUMBER() OVER (PARTITION BY pam.PokemonId 
                                  ORDER BY a.AbilityName) AS abilityNo
      FROM PokemonAbilitiesMapped pam
      JOIN Abilities a
        ON a.AbilityId = pam.AbilityId
)
SELECT p.PokemonName, p.NationalDexNumber, p.PokemonHeight, p.PokemonWeight,
p.Category, p.Gender, p.GenderDifference, p.TypeOne, p.TypeTwo
   , ability1.abilityName AS ability1
   , ability2.abilityName AS ability2
FROM Pokemon p
LEFT OUTER
JOIN abilitiesMapped ability1
  ON ability1.PokemonId = p.PokemonId
 AND ability1.abilityNo = 1
LEFT OUTER
JOIN abilitiesMapped ability2
  ON ability2.PokemonId = p.PokemonId
 AND ability1.abilityNo = 2

CTE abilitiesMapped 获取您的映射和能力,并为每个口袋妖怪 (PARTITION BY pam.PokemonId) 生成一个 abilityNo,其中数字按能力名称 (ORDER BY ...) 排序。然后这个 CTE 被加入两次 abilityNo 1 和 2。LEFT JOIN 很重要,因为口袋妖怪不一定需要有两个能力,在这种情况下你会在 ability2 中得到一个 NULL(INNER JOIN您只会获得具有两种能力的能力,因为 abilityNo = 2 需要存在才能满足连接条件)。

感谢您 post 的创建、插入语句。很有帮助。

为避免能力名称重复,您可以将能力名称设为逗号分隔列表。我正在使用 STR_AGG 生成逗号分隔列表。从 SQL Server 2017 开始可用。对于早期版本,您必须使用 FOR XML PATH 来生成逗号分隔列表。

SELECT p.PokemonName, p.NationalDexNumber, p.PokemonHeight, p.PokemonWeight,
p.Category, p.Gender, p.GenderDifference, p.TypeOne, p.TypeTwo,
STRING_AGG(a.AbilityName,',') AS Abilities
FROM Pokemon p
JOIN PokemonAbilitiesMapped pam
ON p.PokemonId = pam.PokemonId
JOIN Abilities a
ON pam.AbilityId = a.AbilityId
GROUP BY p.PokemonName, p.NationalDexNumber, p.PokemonHeight, p.PokemonWeight,
p.Category, p.Gender, p.GenderDifference, p.TypeOne, p.TypeTwo
PokemonName NationalDexNumber PokemonHeight PokemonWeight Category Gender GenderDifference TypeOne TypeTwo Abilities
Gigantamax Venusaur 3 945 NULL Seed Male / Female 1 Grass Poison Overgrow,Test
Mega Venusaur 3 94 342.80 Seed Male / Female 0 Grass Poison Thick Fat
Venusaur 3 79 220.50 Seed Male / Female 1 Grass Poison Overgrow