2017-02-09 102 views
0

我有一个表project p和表invoice i,它们都有一个project_id字段。我想要一个结果集,其中包含所有来自project的project_id,其中i.status = "Active"还包括来自invoice的所有project_id,其中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项目=活性和在invoice约120项目自2016年1月1日已被开具发票。上述查询返回约140条记录,这听起来正确(一些非发票活动项目和一些非活动发票项目)。问题是查询大约有一半的project_ids为空(NULL?)。就好像它没有从invoice中拉出project_ids。请帮我解决这个问题。

下面是结果设置为阵列的短段...

... 
[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

因为那些是'active'的项目,但没有'invoice_date>'2016-01-01''的发票的相应项目。你可能会遇到这样的情况:发票中没有“活动”项目 – Lamak

+0

在你的选择中包括两个表项目值。我也很疑惑你为什么比较一个字符串值的“日期”?我想它会起作用,因为它是yyyy-mm-dd格式;但对我来说使用日期数据类型比较日期似乎更安全。 – xQbert

回答

2

变化

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

,你不会有任何空值。空值是发票中的值,但不是项目。

+1

Gordan的回答比较好,但我会留下这个,希望它能帮助你理解问题所在。 – 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 distinctinvoice表,如果一个项目可能有不止一个索引。

+0

我听说你在这里大喊大叫。 – Hogan

+0

我读到日期字面值应该是'YYYYMMDD'在SQL Server中(没有破折号),所以必须是'20160101'。 –

+0

@ThorstenKettner。 。 。 YYYYMMDD在技术上是SQL Server中日期常量的最佳形式,因为无论国际化设置如何,它都是明确的。我更喜欢YYYY-MM-DD,因为(1)它(也)符合ISO 8601; (2)我是一个人(尽管有相反的传言),而且它更容易阅读; (3)它得到各种数据库的支持。 –