2014-11-17 2 views
0

Я унаследовал некоторый забавный SQL и пытаюсь понять, как устранить строки с дублирующимися идентификаторами. Наши индексы хранятся в несколько столбчатых форматах, а затем мы сворачиваем все строки в один со значениями в виде разных столбцов.SQL возвращает только отдельные идентификаторы от LEFT JOIN

Нижеприведенный образец возвращает три строки уникальных данных, но идентификаторы дублируются. Мне нужно всего две строки с уникальными идентификаторами (и другими столбцами, которые идут вместе с ним). Я знаю, что я потеряю некоторые данные, но мне просто нужна одна соответствующая строка для каждого идентификатора для запроса (сначала, сверху, самой старой, самой новой, любой).

Я пробовал использовать DISTINCT, GROUP BY и ROW_NUMBER, но я продолжаю получать синтаксис неправильно или использовать их не в том месте.

Я также могу полностью переписать запрос таким образом, который можно использовать повторно, поскольку в настоящее время мне приходится генерировать это «на лету» (типы карт и cardindexes определены пользователем) и хотели бы иметь возможность создавать хранимую процедуру. Заранее спасибо!

declare @cardtypes table ([ID] int, [Name] nvarchar(50)) 
declare @cards table ([ID] int, [CardTypeID] int, [Name] nvarchar(50)) 
declare @cardindexes table ([ID] int, [CardID] int, [IndexType] int, [StringVal] nvarchar(255), [DateVal] datetime) 

INSERT INTO @cardtypes VALUES (1, 'Funny Cards') 
INSERT INTO @cardtypes VALUES (2, 'Sad Cards') 

INSERT INTO @cards VALUES (1, 1, 'Bunnies') 
INSERT INTO @cards VALUES (2, 1, 'Dogs') 
INSERT INTO @cards VALUES (3, 1, 'Cat') 
INSERT INTO @cards VALUES (4, 1, 'Cat2') 

INSERT INTO @cardindexes VALUES (1, 1, 1, 'Bunnies', null) 
INSERT INTO @cardindexes VALUES (2, 1, 1, 'playing', null) 
INSERT INTO @cardindexes VALUES (3, 1, 2, null, '2014-09-21') 
INSERT INTO @cardindexes VALUES (4, 2, 1, 'Dogs', null) 
INSERT INTO @cardindexes VALUES (5, 2, 1, 'playing', null) 
INSERT INTO @cardindexes VALUES (6, 2, 1, 'poker', null) 
INSERT INTO @cardindexes VALUES (7, 2, 2, null, '2014-09-22') 


SELECT TOP(100) 
    [ID] = c.[ID], 
    [Name] = c.[Name], 
    [Keyword] = [colKeyword].[StringVal], 
    [DateAdded] = [colDateAdded].[DateVal] 
FROM @cards AS c 
LEFT JOIN @cardindexes AS [colKeyword] ON [colKeyword].[CardID] = c.ID AND [colKeyword].[IndexType] = 1 
LEFT JOIN @cardindexes AS [colDateAdded] ON [colDateAdded].[CardID] = c.ID AND [colDateAdded].[IndexType] = 2 
WHERE [colKeyword].[StringVal] LIKE 'p%' AND c.[CardTypeID] = 1 
ORDER BY [DateAdded] 

Edit:

Хотя оба решения справедливы, я закончил с использованием раствора MAX() из @popovitsj, как это было легче реализовать. Проблема с данными, поступающими из нескольких строк, на самом деле не влияет на меня, поскольку все строки по существу являются частью одной и той же записи. Я, скорее всего, буду использовать оба решения в зависимости от моих потребностей.

Вот мой обновленный запрос (как это не совсем соответствует ответу):

SELECT TOP(100) 
    [ID] = c.[ID], 
    [Name] = MAX(c.[Name]), 
    [Keyword] = MAX([colKeyword].[StringVal]), 
    [DateAdded] = MAX([colDateAdded].[DateVal]) 
FROM @cards AS c 
LEFT JOIN @cardindexes AS [colKeyword] ON [colKeyword].[CardID] = c.ID AND [colKeyword].[IndexType] = 1 
LEFT JOIN @cardindexes AS [colDateAdded] ON [colDateAdded].[CardID] = c.ID AND [colDateAdded].[IndexType] = 2 
WHERE [colKeyword].[StringVal] LIKE 'p%' AND c.[CardTypeID] = 1 
GROUP BY c.ID 
ORDER BY [DateAdded] 

ответ

2

Вы можете использовать MAX или MIN, чтобы «решить» на то, что для отображения для других столбцов в строках, которые дубликат.

SELECT ID, MAX(Name), MAX(Keyword), MAX(DateAdded) 
(...) 
GROUP BY ID; 
+0

Я бы также добавил ORDER BY MAX (DateAdded), чтобы сохранить тот же порядок. На самом деле, я думаю, что вопрос повторяется до http://stackoverflow.com/questions/5391564/how-to-use-distinct-and-order-by-in-same-select-statement – sarh

+0

Это может смешивать данные, хотя .. строка, которую вы вернетесь с вашим идентификатором, может не соответствовать ни одному из исходных строк. – Greenspark

+0

Правда, это зависит от точных требований, если это хорошее решение. – wvdz

2

используя номер строки оконных функции наряду с КТРОМ будет делать это очень хорошо. Например:

;With preResult AS (
SELECT TOP(100) 
    [ID] = c.[ID], 
    [Name] = c.[Name], 
    [Keyword] = [colKeyword].[StringVal], 
    [DateAdded] = [colDateAdded].[DateVal], 
    ROW_NUMBER()OVER(PARTITION BY c.ID ORDER BY [colDateAdded].[DateVal]) rn 
FROM @cards AS c 
LEFT JOIN @cardindexes AS [colKeyword] ON [colKeyword].[CardID] = c.ID AND [colKeyword].[IndexType] = 1 
LEFT JOIN @cardindexes AS [colDateAdded] ON [colDateAdded].[CardID] = c.ID AND [colDateAdded].[IndexType] = 2 
WHERE [colKeyword].[StringVal] LIKE 'p%' AND c.[CardTypeID] = 1 
ORDER BY [DateAdded] 
) 

SELECT * from preResult WHERE rn = 1 
+0

Это, как представляется, работает. Можете ли вы сказать мне, какие недостатки делают это таким образом? –

+1

Это намного лучший способ выбрать один ряд из многих в присутствии дубликатов, чем MAX в сочетании с техникой GROUP BY. MAX в сочетании с методом GROUP BY может в конечном итоге обеспечить слияние двух или более строк, и пользователи вряд ли захотят или ожидают этого. Если они хотят и ожидают слияния, что я сомневаюсь, перейдите по маршруту MAX/GROUP BY. Вместо этого я использую ROW_NUMBER(). В DB2 мы также можем использовать LATERAL-соединение для достижения того же, что является еще одним отличным инструментом для выбора одной строки из многих, но я думаю, что SQL Server называет ее чем-то другим. –