2015-06-29 3 views
0

Пожалуйста, не уменьшайте это, так как мне сложно объяснить. Я работаю над миграцией данных, поэтому некоторые из структур выглядят странно, потому что они были созданы кем-то подобным.ВЫБОР ЗАЯВОК В СЛУЧАЕ

Например, у меня есть стол Person с PersonID и PersonName в виде столбцов. У меня есть дубликаты в таблице.

У меня есть таблица сведений, в которой у меня есть имя пользователя, хранящееся в столбце. Это имя PersonName может существовать или не существовать в таблице Person. Мне нужно получить PersonID из сопоставимых записей, иначе в PersonID укажу некоторое значение hardcode.

Я не могу написать ниже запрос, потому что PersonName дублируется в таблице Person, это объединение удваивает строки, если из-за присоединения имеется соответствующая запись.

SELECT d.Fields, PersonID 
FROM Details d 
JOIN Person p ON d.PersonName = p.PersonName 

Нижеприведенные работы запроса, но я не знаю, как заменить «NULL» с некоторым значением Я хочу вместо

SELECT d.Fields, (SELECT TOP 1 PersonID FROM Person where PersonName = d.PersonName) 
FROM Details d 

Так NULL, есть некоторые PersonNames в таблице деталей, в таблице Person отсутствуют. Как написать CASE КОГДА в этом случае?

Я попытался ниже, но это не сработало

SELECT d.Fields, 
     CASE WHEN (SELECT TOP 1 PersonID 
       FROM Person 
       WHERE PersonName = d.PersonName) = null 
     THEN 123 
     ELSE (SELECT TOP 1 PersonID 
       FROM Person 
       WHERE PersonName = d.PersonName) END Name 
    FROM Details d 

Этот запрос еще показывает тот же результат, как 2-го запроса. Пожалуйста, посоветуйте мне об этом. Дай мне знать, если я где-то неясен. Спасибо

+0

'= null' никогда не будет оцениваться как истинный, вам нужно сказать' IS NULL'. Также шаблон, который вы используете, неэффективен и неинтуитивен. Вы должны попробовать внешнее соединение вместо всех этих подробных подзапросов. –

+1

Плохая привычка писать такие подзапросы: '(SELECT TOP 1 PersonID FROM Person, где PersonName = d.PersonName)' - попытайтесь привыкнуть к использованию псевдонимов внутри них - '(SELECT TOP 1 p.PersonID FROM Person p, где p.PersonName = d.PersonName) '- если вы неправильно указали имена столбцов, но тот же столбец существует во внешнем запросе, он будет использоваться без предупреждения, и это обычно неверно (например, если не было столбца' PersonName' в 'Person', ваше предложение' WHERE' эффективно превращается в 'where d.PersonName = d.PersonName') –

+0

благодарит Damien за ценную точку. – techspider

ответ

1

Простой левый внешнее соединение, чтобы вытащить обратно все лицо, с дополнительным матчем на столе детали должен работать с сазом, чтобы получить нужный результат.

SELECT 
    * 
FROM 
(
    SELECT 
     Instance=ROW_NUMBER() OVER (PARTITION BY PersonName), 
     PersonID=CASE WHEN d.PersonName IS NULL THEN 'XXXX' ELSE p.PersonID END, 
     d.Fields 
    FROM 
     Person p 
     LEFT OUTER JOIN Details d on d.PersonName=p.PersonName 
)AS X 
WHERE 
    Instance=1 
+0

Обратите внимание, что это приведет к возврату нескольких строк для любого человека с более чем одной строкой Details. –

+0

Есть несколько строк в таблице Details, поэтому я не могу использовать какое-либо соединение, чтобы доверять здесь. – techspider

+0

Я отредактировал ответ на работу, как вы описали. –

2

хорошо .. Я решил, что могу установить ISNULL поверх SELECT, чтобы он работал.

SELECT d.Fields, 
    ISNULL(SELECT TOP 1 p.PersonID 
      FROM Person p where p.PersonName = d.PersonName, 124) id 
    FROM Details d 
+1

* В общем *, предпочитайте ['COALESCE'] (https://msdn.microsoft.com/en-GB/library/ms190349.aspx) через' ISNULL' - он обобщает на более чем 2 входа, он использует обычный правила приоритета типов данных, и это стандартный SQL. –

0

Ooh goody, шанс использовать два LEFT JOIN s. В первом списке будут указаны идентификаторы, в которых они существуют, и в противном случае вставить значение по умолчанию; второй устранит дубликаты.

SELECT d.Fields, ISNULL(p1.PersonID, 123) 
    FROM Details d 
LEFT JOIN Person p1 ON d.PersonName = p1.PersonName 
LEFT JOIN Person p2 ON p2.PersonName = p1.PersonName 
        AND p2.PersonID < p1.PersonID 
    WHERE p2.PersonID IS NULL 
0

Вы можете использовать общие выражения таблицы, чтобы создать недостающие наборы данных, то есть ваша полная таблица Person, а затем присоединиться, что к вашей детализации таблице следующим образом;

declare @n int; 
-- set your default PersonID here; 
set @n = 123; 

-- Make sure previous SQL statement is terminated with semilcolon for with clause to parse successfully. 

-- First build our unique list of names from table Detail. 
with cteUniqueDetailPerson 
(
    [PersonName] 
) 
as 
(
    select distinct [PersonName] 
    from [Details] 
) 

-- Second get unique Person entries and record the most recent PersonID value as the active Person. 
, cteUniquePersonPerson 
(
     [PersonID] 
    , [PersonName] 
) 
as 
(
    select 
      max([PersonID]) -- if you wanted the original Person record instead of the last, change this to min. 
     , [PersonName] 
    from [Person] 
    group by [PersonName] 
) 

-- Third join unique datasets to get the PersonID when there is a match, otherwise use our default id @n. 
-- NB, this would also include records when a Person exists with no Detail rows (they are filtered out with the final inner join) 
, cteSudoPerson 
(
     [PersonID] 
    , [PersonName] 
) 
as 
(
    select 
     coalesce(upp.[PersonID],@n) as [PersonID] 
     coalesce(upp.[PersonName],udp.[PersonName]) as [PersonName] 
    from cteUniquePersonPerson upp 
    full outer join cteUniqueDetailPerson udp 
     on udp.[PersonName] = p.[PersonName] 
) 

-- Fourth, join detail to the sudo person table that includes either the original ID or our default ID. 
select 
     d.[Fields] 
    , sp.[PersonID] 
from [Details] d 
inner join cteSudoPerson sp 
    on sp.[PersonName] = d.[PersonName];