Использование таблиц промежуточного соединения в сложном запросе SQL

#sql #join #sql-server-2019

#sql #Присоединиться #sql-сервер-2019

Вопрос:

Я унаследовал проект от делового партнера и пытаюсь добавить к нему, но здесь я бегу против ветра. Это коктейльная база данных, состоящая из 7 таблиц.

 drinks (ID, drinkName, lu_category, lu_glassware, lu_IBA)
category (categoryID, category)
glassware (glasswareID, glassware)
ingredients (ingredientID, ingredient, notes)
measure (measureID, measure)
IBA (IBAID, IBA)
ingredient_drinks_measure (idmID, drinkID, ingredientID, measureID)
  

Этот запрос работает как ожидалось и возвращает все в одну строку для каждого напитка.

 SELECT        dbo.drinks.id AS [drinkID], 
              dbo.drinks.drinkName, 
              dbo.category.category, 
              dbo.glassware.glassware, 
              dbo.IBA.IBA, 
              string_agg(measure.measure   ' '   ingredients.ingredient, ', ') as [Ingredients]
FROM          dbo.glassware 
              RIGHT OUTER JOIN dbo.measure 
              LEFT OUTER JOIN dbo.ingredient_drinks_measure ON dbo.measure.measureID = dbo.ingredient_drinks_measure.measureID 
              RIGHT OUTER JOIN dbo.ingredients ON dbo.ingredient_drinks_measure.ingredientID = dbo.ingredients.ingredientID 
              RIGHT OUTER JOIN dbo.drinks ON dbo.ingredient_drinks_measure.drinkID = dbo.drinks.id 
              LEFT OUTER JOIN dbo.IBA ON dbo.drinks.lu_IBA = dbo.IBA.IBAID ON dbo.glassware.glasswareID = dbo.drinks.lu_glassware 
              LEFT OUTER JOIN dbo.category ON dbo.drinks.lu_category = dbo.category.categoryID
GROUP BY      dbo.drinks.id, 
              dbo.drinks.drinkName, 
              dbo.drinks.imagePath, 
              dbo.drinks.dateModified, 
              dbo.category.category, 
              dbo.glassware.glassware, 
              dbo.IBA.IBA
  

ВОЗВРАТ

 drinkID | drinkName | category  | glassware         | IBA                   | ingredients
11000   | Mojito    | Cocktail  | Highball glass    | Contemporary Classics | 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar   
  

Теперь мне нужно добавить ТЕГИ к каждому напитку
На данный момент существует только 22 тега, и с каждым напитком может быть связано от 1 до 22 тегов (теги типа «Хэллоуин», «Рождество», «Чаша для пунша», «Завтрак», «Званый ужин», «Фруктовый», «Пьяный» и т.д.).

У меня есть tags table (id, tag) и у меня есть drinkTags table (drinkID, tagID) , сидящий посередине, чтобы объединить напитки и теги вместе (сценарии СОЗДАНИЯ для обоих приведены ниже).

Однако, как бы я ни старался, когда я добавляю объединения к запросу и сопутствующий столбец string_agg для ТЕГОВ (разделенный запятыми сводный столбец), по крайней мере, один из столбцов string_agg дублируется и / или я получаю несколько строк для каждого напитка. например, это:

 drinkID | drinkName | category  | glassware         | IBA                   | ingredients
11000   | Mojito    | Cocktail  | Highball glass    | Contemporary Classics | 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar, 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar, 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar
  

или это:

 drinkID | drinkName | category  | glassware         | IBA                   | tags      | ingredients
11000   | Mojito    | Cocktail  | Highball glass    | Contemporary Classics | Alcoholic | 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar
11000   | Mojito    | Cocktail  | Highball glass    | Contemporary Classics | IBA       | 2 tsp Superfine Sugar, Juice of 1 Lime, 2-3 oz Jamaican Rum
11000   | Mojito    | Cocktail  | Highball glass    | Contemporary Classics | USA       | 2-3 oz Jamaican Rum, Juice of 1 Lime, 2 tsp Superfine Sugar
  

Есть мысли? Я был бы признателен за любую помощь, даже если это просто указывает мне на то, что я упустил из виду во всех своих исследованиях.

Вот сценарии СОЗДАНИЯ, если они вам нужны.

 /****** Object:  Table [dbo].[drinks]    Script Date: 9/15/2020 1:25:57 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[drinks](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [drinkName] [varchar](37) NOT NULL,
    [lu_category] [int] NULL,
    [lu_IBA] [int] NULL,
    [lu_glassware] [int] NULL
 CONSTRAINT [PK__drinks_c__2B658F5CD9E4315A] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

/****** Object:  Table [dbo].[category]    Script Date: 9/15/2020 1:27:18 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[category](
    [categoryID] [int] IDENTITY(1,1) NOT NULL,
    [category] [nvarchar](50) NULL,
 CONSTRAINT [PK__category__23CAF1F80317C8FA] PRIMARY KEY CLUSTERED 
(
    [categoryID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO



/****** Object:  Table [dbo].[drinkTags]    Script Date: 9/15/2020 1:27:54 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[drinkTags](
    [drinkID] [int] NOT NULL,
    [tagID] [int] NOT NULL,
 CONSTRAINT [PK_drinkTags_1] PRIMARY KEY CLUSTERED 
(
    [drinkID] ASC,
    [tagID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO




/****** Object:  Table [dbo].[glassware]    Script Date: 9/15/2020 1:28:08 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[glassware](
    [glasswareID] [int] IDENTITY(1,1) NOT NULL,
    [glassware] [nvarchar](50) NULL,
PRIMARY KEY CLUSTERED 
(
    [glasswareID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO



/****** Object:  Table [dbo].[IBA]    Script Date: 9/15/2020 1:28:19 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[IBA](
    [IBAID] [int] IDENTITY(1,1) NOT NULL,
    [IBA] [nvarchar](50) NULL,
PRIMARY KEY CLUSTERED 
(
    [IBAID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO



/****** Object:  Table [dbo].[ingredient_drinks_measure]    Script Date: 9/15/2020 1:28:32 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[ingredient_drinks_measure](
    [idmID] [int] IDENTITY(1,1) NOT NULL,
    [drinkID] [int] NULL,
    [ingredientID] [int] NULL,
    [measureID] [int] NULL,
PRIMARY KEY CLUSTERED 
(
    [idmID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO



/****** Object:  Table [dbo].[ingredients]    Script Date: 9/15/2020 1:28:44 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[ingredients](
    [ingredientID] [int] IDENTITY(1,1) NOT NULL,
    [ingredient] [nvarchar](150) NULL
PRIMARY KEY CLUSTERED 
(
    [ingredientID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO



/****** Object:  Table [dbo].[measure]    Script Date: 9/15/2020 1:29:14 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[measure](
    [measureID] [int] IDENTITY(1,1) NOT NULL,
    [measure] [nvarchar](50) NULL,
PRIMARY KEY CLUSTERED 
(
    [measureID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO



/****** Object:  Table [dbo].[tags]    Script Date: 9/15/2020 1:29:25 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[tags](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [tag] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_drinkTags] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
  

Ответ №1:

Решение 1:

Имеет ли напиток однозначную связь с тегом? Если это так, то все, что вам нужно сделать, это добавить столбец тегов в таблицу напитков. Это будет ненормализованный способ решения проблемы.

Решение 2:

Если у drink нет однозначной связи с объектом tag.
В какой таблице хранится взаимосвязь между напитками и тегами? Если у вас нет этой таблицы, вам нужно создать ее и заполнить данными. Если вам нужна помощь в создании отношения drinks к тегу, я могу помочь вам с этим. Затем вам нужно будет обновить SQL, чтобы использовать отношение tag to drink для получения единой записи, а также добавить поле tag в SQL.

Комментарии:

1. У каждого напитка может быть много тегов, поэтому это определенно не отношение 1 к 1. Существует таблица тегов, в которой хранятся идентификатор тега и имя тега, также существует таблица, которую я назвал drinkTags, в которой хранятся идентификатор drinkID и идентификатор TagID. Итак, в продолжение примеров, которые я привел в своем посте, таблица drinkTags будет содержать 3 записи для drinkID # 11000. Это объединение этих двух таблиц и добавление столбца string_agg, который, кажется, дает мне подгонку.

Ответ №2:

Надеюсь, приведенный ниже запрос решит вашу проблему. Я добавил одно поле string_agg, чтобы возвращать все теги, связанные с напитком, и два ВНУТРЕННИХ СОЕДИНЕНИЯ.

 SELECT        dbo.drinks.id AS [drinkID], 
              dbo.drinks.drinkName, 
              dbo.category.category, 
              dbo.glassware.glassware, 
              dbo.IBA.IBA, 
              string_agg(measure.measure   ' '   ingredients.ingredient, ', ') as [Ingredients],
          string_agg(dbo.tags.tag, ', ‘) as [Tags]
FROM          dbo.glassware 
              RIGHT OUTER JOIN dbo.measure 
              LEFT OUTER JOIN dbo.ingredient_drinks_measure ON dbo.measure.measureID = dbo.ingredient_drinks_measure.measureID 
              RIGHT OUTER JOIN dbo.ingredients ON dbo.ingredient_drinks_measure.ingredientID = dbo.ingredients.ingredientID 
              RIGHT OUTER JOIN dbo.drinks ON dbo.ingredient_drinks_measure.drinkID = dbo.drinks.id 
              LEFT OUTER JOIN dbo.IBA ON dbo.drinks.lu_IBA = dbo.IBA.IBAID ON dbo.glassware.glasswareID = dbo.drinks.lu_glassware 
              LEFT OUTER JOIN dbo.category ON dbo.drinks.lu_category = dbo.category.categoryID
          INNER JOIN  dbo.drinkTags.drinkID =  dbo.drinks.id    
          INNER JOIN  dbo.tags.id = dbo.drinkTags.tagID
GROUP BY      dbo.drinks.id, 
              dbo.drinks.drinkName, 
              dbo.drinks.imagePath, 
              dbo.drinks.dateModified, 
              dbo.category.category, 
              dbo.glassware.glassware, 
              dbo.IBA.IBA
  

Комментарии:

1. Большое вам спасибо, но, боюсь, это очень похоже на результаты, которые я получаю. В этом случае столбец Ingredients и столбец Tags дублировались внутри string_agg, равного количеству элементов, которые необходимо объединить: например, в столбце, разделенном запятыми, три тега (Alcohol, IBA, USA) повторяются три раза. То же самое верно для 3 ингредиентов (ром, лайм, сахар), которые повторяются 3 раза в столбце, разделенном запятыми.

Ответ №3:

После гораздо большего исследования я нашел решение, которое будет работать для меня. Это делает предположение, что для каждого drinkID есть хотя бы один тег, но на данный момент я могу с этим справиться.

 ;WITH a AS
(
SELECT 
    d.id,
    d.drinkName as [Name] ,
    c.category as [Category],
    g.glassware as [Glass],
    b.IBA as [IBA],
    string_agg(m.measure   ' '   i.ingredient, ', ') as [Ingredients],
    d.instructions as [Instructions],
    string_agg(i.notes, ', ') as [IngredientNotes],
    d.addedInfo as [Additional Information],
    d.imagePath as [Image]
FROM drinks d
    JOIN category c on c.categoryID = d.lu_category
    JOIN glassware g on g.glasswareID = d.lu_glassware
    LEFT JOIN IBA b on b.IBAID = d.lu_IBA
    JOIN ingredient_drinks_measure idm on idm.drinkID = d.id
    JOIN ingredients i on i.ingredientID = idm.ingredientID
    JOIN measure m on m.measureID = idm.measureID
group by d.id, d.drinkName, c.category, g.glassware, b.IBA, d.addedInfo, d.imagePath, d.instructions
)
SELECT a.*, dtm.tags
FROM a
JOIN (select 
        drinkID, string_agg(t.tag, ', ') as tags
        from tags t
        join tagmap tm on tm.tagID = t.id
        group by drinkID) as dtm
ON a.ID = dtm.drinkID