在 SQL 中将值列表与表行连接起来

Posted

技术标签:

【中文标题】在 SQL 中将值列表与表行连接起来【英文标题】:Joining a list of values with table rows in SQL 【发布时间】:2014-06-16 03:00:08 【问题描述】:

假设我有一个值列表,例如1, 2, 3, 4, 5 和一个表,其中一些值存在于某个列中。这是一个例子:

id  name
 1  Alice
 3  Cindy
 5  Elmore
 6  Felix

我想创建一个SELECT 语句,该语句将包含我列表中的所有值以及与这些值匹配的那些行中的信息,即在我的列表和表之间执行LEFT OUTER JOIN,所以结果如下:

id  name
 1  Alice
 2  (null)
 3  Cindy
 4  (null)
 5  Elmore

如何在不创建临时表或使用多个 UNION 运算符的情况下做到这一点?

【问题讨论】:

什么数据库产品? MS SQL Server 或 Oracle。 【参考方案1】:

如果在 Microsoft SQL Server 2008 或更高版本中,则可以使用Table Value Constructor

 Select v.valueId, m.name 
 From (values (1), (2), (3), (4), (5)) v(valueId)
     left Join otherTable m
        on m.id = v.valueId

Postgres 也有这个构造 VALUES Lists:

SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter)

还要注意可能的Common Table Expression 语法,它可以方便地进行连接:

WITH my_values(num, str) AS (
    VALUES (1, 'one'), (2, 'two'), (3, 'three')
)
SELECT num, txt FROM my_values

使用 Oracle 是可能的,虽然更重From ASK TOM:

with id_list as (
  select 10 id from dual union all
  select 20 id from dual union all
  select 25 id from dual union all
  select 70 id from dual union all
  select 90 id from dual
)
  select * from id_list;

【讨论】:

这在各种情况下都非常有用 有没有办法动态生成一系列 int 而不是硬编码? 在 SQL Server 中工作,但我仍在寻找更有用的命令格式,因为单个值周围的括号...太多了。 @user3079364,动态是什么意思?你的意思是在 T-SQL 中?如果是这样,那么当然,创建一个空表变量,然后使用使用您希望的任何逻辑生成的 T-SQL 插入语句填充它。 @JakeJ,括号(或其他标记)作为每行数据之间的分隔符是必要的。每个括号内将是该单行数据的一组逗号分隔值...如(values (1, 16, 'Dave'), (2, 22, 'Mary'), (3, 8, 'joey'), (4, 55, 'Dad'), (5, 46, 'Mom')) v(PersonId, Age, Name). 没有括号,则需要一些其他标记来分隔行。【参考方案2】:

以下oracle解决方案采用this source。基本思想是利用 oracle 的分层查询。您必须指定列表的最大长度(在下面的示例查询中为 100)。

   select d.lstid
        , t.name
     from (
               select substr(
                           csv
                         , instr(csv,',',1,lev) + 1
                         , instr(csv,',',1,lev+1 )-instr(csv,',',1,lev)-1
                      )  lstid
                 from (select ','||'1,2,3,4,5'||',' csv from dual)
                    , (select level lev from dual connect by level <= 100)
                where lev <= length(csv)-length(replace(csv,','))-1         
          ) d
left join test  t on ( d.lstid = t.id )
        ;

查看this sql fiddle 看看它是否有效。

【讨论】:

【参考方案3】:

这有点晚了,但是对于 Oracle,您可以执行以下操作来获取值表:

SELECT rownum + 5 /*start*/ - 1 as myval
FROM dual
CONNECT BY LEVEL <= 100 /*end*/ - 5 /*start*/ + 1

...然后将其加入您的桌子:

SELECT *
FROM
(SELECT rownum + 1 /*start*/ - 1 myval
FROM dual
CONNECT BY LEVEL <= 5 /*end*/ - 1 /*start*/ + 1) mypseudotable
left outer join myothertable
    on mypseudotable.myval = myothertable.correspondingval

【讨论】:

【参考方案4】:

假设 myTable 是您的表的名称,下面的代码应该可以工作。

;with x as 
(
  select top (select max(id) from [myTable]) number from [master]..spt_values
),
y as
(select row_number() over (order by x.number) as id
from x)
select y.id,  t.name
from y left join myTable as t
on y.id = t.id;

注意:这是 SQL Server 实现。

fiddle

【讨论】:

我不确定我是否理解。值列表(1、2、3、4、5)如何以及在何处发挥作用? 这可以处理从 1 到您的最高 id 可能是 (1,2,3,4,5,....,n) 的任何内容。 抱歉误导了您。我引入id 列只是为了说明。值列表不必表示 ID、rownum 甚至序列号的列表。它可以由任意字符串或任何其他不同的值组成。【参考方案5】:

用于获取部分输出所需的连续数字(此方法消除了为 n 个数字键入的值):

declare @site as int
set @site = 1
while @site<=200
begin
insert into ##table
values (@site)

set @site=@site+1
end

最终输出[post above step]:

select * from ##table
select v.id,m.name from  ##table  as v
left outer join [source_table] m
 on m.id=v.id

【讨论】:

你能修正一下你的问题的格式吗?【参考方案6】:

假设您的表具有值1,2,3,4,5 命名为list_of_values,并假设表包含一些值但名称列为some_values,您可以这样做:

SELECT B.id,A.name
FROM [list_of_values] AS B
LEFT JOIN [some_values] AS A
ON B.ID = A.ID

【讨论】:

问题是值列表不在任何表中。我想我最初的问题归结为:如何将任意值列表转换为可在 join 中使用的子查询?

以上是关于在 SQL 中将值列表与表行连接起来的主要内容,如果未能解决你的问题,请参考以下文章

如何在sql中将同一张表中的两个值连接起来?

如何在 LINQ sql 中将两个表与一个具有不同值的表连接起来?

在 SQL Server 表中搜索值列表的最有效方法

Oracle 连接大列表

如何在 Python 中将表行 PCollections 转换为键、值 PCollections?

你如何在熊猫中将多行连接成一行?