29

我正在尝试构建一个 SQL 语句,该语句从位于同一个表中的多个列返回唯一的非空值。

 SELECT distinct tbl_data.code_1 FROM tbl_data
      WHERE tbl_data.code_1 is not null
 UNION
 SELECT tbl_data.code_2 FROM tbl_data
      WHERE tbl_data.code_2 is not null;

例如,tbl_data 如下:

 id   code_1    code_2
 ---  --------  ----------
 1    AB        BC
 2    BC        
 3    DE        EF
 4              BC

对于上表,SQL 查询应该返回两列中所有唯一的非空值,即:AB、BC、DE、EF。

我对 SQL 很陌生。我上面的语句有效,但是有没有更简洁的方法来编写这个 SQL 语句,因为这些列来自同一个表?

4

4 回答 4

32

最好在您的问题中包含代码,而不是模棱两可的文本数据,以便我们都使用相同的数据。这是我假设的示例架构和数据:

CREATE TABLE tbl_data (
  id INT NOT NULL,
  code_1 CHAR(2),
  code_2 CHAR(2)
);

INSERT INTO tbl_data (
  id,
  code_1,
  code_2
)
VALUES
  (1, 'AB', 'BC'),
  (2, 'BC', NULL),
  (3, 'DE', 'EF'),
  (4, NULL, 'BC');

正如Blorgbeard评论的那样,DISTINCT您的解决方案中的子句是不必要的,因为UNION运算符消除了重复的行。有一个UNION ALL运算符不消除重复,但在这里不合适。

不使用该子句重写查询DISTINCT是解决此问题的好方法:

SELECT code_1
FROM tbl_data
WHERE code_1 IS NOT NULL
UNION
SELECT code_2
FROM tbl_data
WHERE code_2 IS NOT NULL;

两列在同一个表中并不重要。即使列在不同的表中,解决方案也是相同的。

如果您不喜欢两次指定相同过滤子句的冗余,您可以在过滤之前将联合查询封装在虚拟表中:

SELECT code
FROM (
  SELECT code_1
  FROM tbl_data
  UNION
  SELECT code_2
  FROM tbl_data
) AS DistinctCodes (code)
WHERE code IS NOT NULL;

我发现第二个的语法更丑陋,但它在逻辑上更整洁。但是哪一个表现更好呢?

我创建了一个sqlfiddle来演示 SQL Server 2005 的查询优化器为两个不同的查询生成相同的执行计划:

查询优化器为两个查询生成这个执行计划:两个表扫描、一个连接、一个不同的排序和一个选择。

如果 SQL Server 为两个查询生成相同的执行计划,那么它们实际上在逻辑上是等效的。

将上述内容与您问题中查询的执行计划进行比较:

DISTINCT 子句使 SQL Server 2005 执行冗余排序操作。

DISTINCT子句使 SQL Server 2005 执行冗余排序操作,因为查询优化器不知道DISTINCT在第一个查询中过滤掉的任何重复项都会被UNION后面的查询过滤掉。

这个查询在逻辑上与其他两个是等价的,但是冗余操作使其效率降低。在大型数据集上,我希望您的查询需要比这里的两个更长的时间才能返回结果集。不要相信我的话;在您自己的环境中进行实验以确保!

于 2012-07-03T00:31:43.010 回答
5

尝试类似SubQuery

SELECT derivedtable.NewColumn
FROM
(
    SELECT code_1 as NewColumn FROM tbl_data 
    UNION
    SELECT code_2 as NewColumn FROM tbl_data 
) derivedtable
WHERE derivedtable.NewColumn IS NOT NULL

已经从组合查询UNION返回DISTINCT值。

于 2012-07-03T00:09:26.827 回答
0

如果您有两个以上的列,请尝试此操作:

CREATE TABLE #temptable (Name1 VARCHAR(25),Name2 VARCHAR(25))

INSERT INTO #temptable(Name1, Name2)
  VALUES('JON', 'Harry'), ('JON', 'JON'), ('Sam','harry')

SELECT t.Name1+','+t.Name2 Names  INTO #t FROM #temptable AS tSELECT DISTINCT ss.value FROM #t AS t
  CROSS APPLY STRING_SPLIT(T.Names,',') AS ss
于 2019-02-13T14:47:04.363 回答
0

在所需的行数据在类型、值等方面相似的任何地方都应用联合。无论您在同一个表中是否有列或另一个要从中检索的列都没有关系,因为结果将保持不变(在上述答案之一中已经提到尽管)。

由于您不希望重复,因此使用 UNION ALL 并没有必要使用 distinct,因为 union 提供了不同的数据

可以创建视图将是最佳选择,因为视图是表的虚拟表示。然后可以在创建的视图上巧妙地进行修改

Create VIEW getData AS 
(
  SELECT distinct tbl_data.code_1 
    FROM tbl_data
    WHERE tbl_data.code_1 is not null
  UNION
  SELECT tbl_data.code_2 
    FROM tbl_data
    WHERE tbl_data.code_2 is not null
);
于 2019-05-15T11:06:47.173 回答