2017-02-09 4 views
0

У меня есть таблица project p и таблица invoice i, каждая из которых имеет поле project_id. Я хочу, чтобы набор результатов включал все объекты project_ids от project, где i.status = "Active", а также включает в себя все project_ids от invoice, где invoice_date > 2016-01-01. Вот запрос, который я пробовал до сих пор.SQL OUTER JOIN возвращает несколько пустых записей - почему?

SELECT 
    p.project_id 
FROM 
    (SELECT project_id 
     FROM project 
     WHERE status = 'Active') p 
FULL OUTER JOIN 
    (SELECT DISTINCT project_id 
     FROM invoice 
     WHERE CONVERT(varchar(10),invoice_date, 20) > '2016-01-01') i 
ON i.project_id = p.project_id 

Есть в projects со статусом около 80 проектов = активных и около 120 проектов в invoice, которые были выставлены счета, так как 2016-01-01. Вышеприведенный запрос возвращает около 140 записей, которые звучат правильно (некоторые неактивные активные проекты и некоторые неактивные счета-фактуры). Проблема состоит в том, что запрос содержит около половины значений project_ids как пустых (NULL?). Это как будто он не тянет в project_ids от invoice. Пожалуйста, помогите мне исправить это.

Вот короткий отрезок результирующего набора в виде массива ...

... 
[10] => Array 
    (
     [project_id] => 
    ) 

[11] => Array 
    (
     [project_id] => C00F2097-CD36-4497-8B26-0BF59F90B1EA 
    ) 

[12] => Array 
    (
     [project_id] => 217F3370-50F2-457E-A4F5-0C09F12E654A 
    ) 

[13] => Array 
    (
     [project_id] => 
    ) 

[14] => Array 
    (
     [project_id] => B1A06823-73C8-4691-A3D6-0E1A234516B3 
etc... 
+0

потому что это проекты, в которых они активны, но не имеют соответствующего проекта из счета-фактуры, где 'invoice_date> '2016-01-01''. И у вас могут быть случаи, когда для счетов-фактур нет 'активных' проектов. – Lamak

+0

Включите оба значения проекта в таблицу в свой выбор. Я также озадачен тем, почему вы сравниваете «даты» со строковым значением? Я полагаю, что это сработает, так как это формат yyyy-mm-dd; но мне кажется более безопасным сравнивать даты с использованием типа даты. – xQbert

ответ

2

изменение

SELECT coalesce(p.project_id, i.project_id) as project_id 

, и вы не будете иметь никаких нулевых значений. Нулевые значения являются значениями в счете-фактуре, но не для проекта.

+1

Ответ Гордана лучше, но я собираюсь оставить это в надежде, что он поможет вам понять, в чем проблема. – Hogan

3

Ваше описание просто кричит «используйте UNION, используйте UNION»!

SELECT p.project_id 
FROM project p 
WHERE p.status = 'Active' 
UNION -- On purpose to remove duplicates 
SELECT i.project_id 
FROM invoice i 
WHERE i.invoice_date > '2016-01-01'; 

Примечание: нет необходимости преобразовывать дату в строку для сравнения с постоянным значением. На самом деле это не очень хорошая идея (это предотвращает использование индексов).

Вы также можете рассказать это, используя UNION ALL. Это, вероятно, самый дешевый способ получить то, что вы хотите (при условии, что таблицы имеют соответствующие индексы):

SELECT p.project_id 
FROM project p 
WHERE p.status = 'Active' 
UNION ALL 
SELECT i.project_id 
FROM invoice i 
WHERE invoice_date > '2016-01-01' AND 
     NOT EXISTS (SELECT 1 
        FROM project p2 
        WHERE p2.project_id = i.project_id AND p.status = 'Active' 
       ); 

В этом случае, возможно, потребуется select distinct в invoice таблице, если проект может иметь более одного индекса.

+0

Я слышал, как вы кричали, что все это происходит здесь. – Hogan

+0

Я читал, что литералы даты должны быть «YYYYMMDD» в SQL Server (без тире), поэтому это должно быть «20160101». –

+0

@ThorstenKettner. , , YYYYMMDD технически является лучшей формой для констант даты в SQL Server, поскольку она однозначна, независимо от настроек интернационализации. Я предпочитаю YYYY-MM-DD, потому что (1) он (также) соответствует ISO 8601; (2) Я человек (несмотря на слухи об обратном), и его легче читать; (3) поддержка большого числа баз данных. –