我遇到与this question中描述的问题几乎相同的问题。我有两个具有相同行数的表,我想一个接一个地加入它们。
这些表是有序的,如果可能,我想在加入后保留此订单。
对于MSSql有一个基于rowid的解决方案,但是如果表来自WITH语句(或RECURSIVE WITH),则不能使用SQLite rowid。
保证两个表具有完全相同的行数,但这个数字事先是未知的。同样重要的是要注意,相同的元素可能会出现两次以上。结果是有序的,但没有一列是唯一的。
示例代码:
WITH
table_a (n) AS (
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
)
SELECT table_a.n, table_b.s
FROM table_a
LEFT JOIN table_b ON ( table_a.rowid = table_b.rowid )
我想要达到的结果是:
(2, 'valuex'),
(4, 'valuey'),
(5, 'valuez')
SQLFiddle:http://sqlfiddle.com/#!5/9eecb7/6888
答案 0 :(得分:1)
由于表是有序的,您可以通过比较n个值来添加row_id值。
但是,为了获得更好的性能,最好的方法是在创建表时插入ID值。
http://sqlfiddle.com/#!5/9eecb7/7014
WITH
table_a_a (n, id) AS
(
WITH table_a (n) AS
(
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
)
SELECT table_a.n, (select count(1) from table_a b where b.n <= table_a.n) id
FROM table_a
) ,
table_b_b (n, id) AS
(
WITH table_a (n) AS
(
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
)
SELECT table_a.n, (select count(1) from table_a b where b.n <= table_a.n) id
FROM table_a
)
select table_a_a.n,table_b_b.n from table_a_a,table_b_b where table_a_a.ID = table_b_b.ID
或将输入集转换为逗号分隔列表并尝试如下:
http://sqlfiddle.com/#!5/9eecb7/7337
WITH RECURSIVE table_b( id,element, remainder ) AS (
SELECT 0,NULL AS element, 'valuex,valuey,valuz,valuz' AS remainder
UNION ALL
SELECT id+1,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, 0, INSTR( remainder, ',' ) )
ELSE
remainder
END AS element,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, INSTR( remainder, ',' )+1 )
ELSE
NULL
END AS remainder
FROM table_b
WHERE remainder IS NOT NULL
),
table_a( id,element, remainder ) AS (
SELECT 0,NULL AS element, '2,4,5,7' AS remainder
UNION ALL
SELECT id+1,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, 0, INSTR( remainder, ',' ) )
ELSE
remainder
END AS element,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, INSTR( remainder, ',' )+1 )
ELSE
NULL
END AS remainder
FROM table_a
WHERE remainder IS NOT NULL
)
SELECT table_b.element, table_a.element FROM table_b, table_a WHERE table_a.element IS NOT NULL and table_a.id = table_b.id;
答案 1 :(得分:1)
<强> SQL 强>
SELECT a1.n, b1.s
FROM table_a a1
LEFT JOIN table_b b1
ON (SELECT COUNT(*) FROM table_a a2 WHERE a2.n <= a1.n) =
(SELECT COUNT(*) FROM table_b b2 WHERE b2.s <= b1.s)
<强>解释强>
查询只计算每个表的当前行数(基于排序列)并加入此值。
<强>演示强>
请参阅SQL Fiddle demo。
<强>假设强>
table_a
包含与table_b
相同或更多的行。 (如果情况不是那么a FULL OUTER JOIN
would need to be emulated,因为SQLite没有提供。{/ li>
答案 2 :(得分:1)
这在SQLite中非常复杂 - 因为你允许重复。但你可以做到。这是一个想法:
join
关联值并找出重叠。以下代码假定您订购了n
和s
- 正如您在问题中指定的那样。但是,如果另一列指定了排序,它将起作用(稍作修改)。
您会注意到我在示例数据中包含了重复项:
WITH table_a (n) AS (
SELECT 2 UNION ALL
SELECT 4 UNION ALL
SELECT 4 UNION ALL
SELECT 4 UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex' UNION ALL
SELECT 'valuey' UNION ALL
SELECT 'valuey' UNION ALL
SELECT 'valuez' UNION ALL
SELECT 'valuez'
),
a as (
select a.n, count(*) as a_cnt,
(select count(*) from table_a a2 where a2.n < a.n) as a_offset
from table_a a
group by a.n
),
b as (
select b.s, count(*) as b_cnt,
(select count(*) from table_b b2 where b2.s < b.s) as b_offset
from table_b b
group by b.s
),
ab as (
select a.*, b.*,
max(a.a_offset, b.b_offset) as offset,
min(a.a_offset + a.a_cnt, b.b_offset + b.b_cnt) - max(a.a_offset, b.b_offset) as cnt
from a join
b
on a.a_offset + a.a_cnt - 1 >= b.b_offset and
a.a_offset <= b.b_offset + b.b_cnt - 1
),
cte as (
select n, s, offset, cnt, 1 as ind
from ab
union all
select n, s, offset, cnt, ind + 1
from cte
where ind < cnt
)
select n, s
from cte
order by n, s;
Here是一个DB小提琴,显示结果。
我应该注意到,使用窗口函数(或MySQL中的变量)几乎可以在任何其他数据库中更简单。
答案 3 :(得分:0)
无论哪种方式......
使用类似
的内容WITH
v_table_a (n, rowid) AS (
SELECT 2, 1
UNION ALL
SELECT 4, 2
UNION ALL
SELECT 5, 3
),
v_table_b (s, rowid) AS (
SELECT 'valuex', 1
UNION ALL
SELECT 'valuey', 2
UNION ALL
SELECT 'valuez', 3
)
SELECT v_table_a.n, v_table_b.s
FROM v_table_a
LEFT JOIN v_table_b ON ( v_table_a.rowid = v_table_b.rowid );
表示“虚拟”表格(WITH
或没有),
WITH RECURSIVE vr_table_a (n, rowid) AS (
VALUES (2, 1)
UNION ALL
SELECT n + 2, rowid + 1 FROM vr_table_a WHERE rowid < 3
)
, vr_table_b (s, rowid) AS (
VALUES ('I', 1)
UNION ALL
SELECT s || 'I', rowid + 1 FROM vr_table_b WHERE rowid < 3
)
SELECT vr_table_a.n, vr_table_b.s
FROM vr_table_a
LEFT JOIN vr_table_b ON ( vr_table_a.rowid = vr_table_b.rowid );
使用递归WITH
的“虚拟”表的(在这个例子中,值是其他的,然后是你的,但我想你明白了)和
CREATE TABLE p_table_a (n INT);
INSERT INTO p_table_a VALUES (2), (4), (5);
CREATE TABLE p_table_b (s VARCHAR(6));
INSERT INTO p_table_b VALUES ('valuex'), ('valuey'), ('valuez');
SELECT p_table_a.n, p_table_b.s
FROM p_table_a
LEFT JOIN p_table_b ON ( p_table_a.rowid = p_table_b.rowid );
用于物理表。
但是,我会对最后一个小心。快速测试显示,rowid
的数量是a)重用 - 当某些行被删除而其他行被插入时,插入的行从旧行中获取rowid
s(即{{1在SQLite中,在行的生命周期内并不是唯一的,而例如Oracle的rowid
AFAIR是 - )和b)对应于插入的顺序。但我不知道,也没有在文档中找到线索,如果这有保证或在其他/未来的实现中可能会有变化。或许这只是我测试环境中的巧合。
(一般情况下,行的物理顺序可能会发生变化(即使在同一个数据库中由于某些重组而使用相同的DMBS),因此也不是一个好的选择依赖。而且不能保证,查询将会返回由表中的物理位置排序的结果(它可能使用某个索引的顺序,或者以某种其他方式排序影响输出顺序的部分结果)。考虑使用相应行中的公共(排序)键设计表订购和加入。)
答案 4 :(得分:0)
答案 5 :(得分:0)
可以在with语句中使用rowid,但是您需要选择它并使其可用于查询。 像这样:
with tablea AS (
select id, rowid AS rid from someids),
tableb AS (
select details, rowid AS rid from somedetails)
select tablea.id, tableb.details
from
tablea
left join tableb on tablea.rid = tableb.rid;
然而,正如他们已经警告过你一个非常糟糕的主意。如果应用程序在插入一个表但在另一个表之前插入时中断了怎么办?如果删除旧行怎么办?如果要连接两个表,则需要指定要执行此操作的字段。这个设计可能会出现很多问题。与此类似的是增量id字段,您可以将其保存在表中并在应用程序中使用。更简单的是,将它们放入一张桌子中。
有关rowid的更多信息,请阅读此链接:https://www.sqlite.org/lang_createtable.html#rowid
sqlfiddle:http://sqlfiddle.com/#!7/29fd8/1
答案 6 :(得分:0)
可以在with语句中使用rowid,但是您需要选择它并使其可用于查询。像这样:
with tablea AS (select id, rowid AS rid from someids),
tableb AS (select details, rowid AS rid from somedetails)
select tablea.id, tableb.details
from
tablea
left join tableb on tablea.rid = tableb.rid;
答案 7 :(得分:0)
问题说明指出:
表已排序
如果这意味着该排序是由UNION ALL
语句中的值的排序定义的,并且如果SQLite遵守该排序,则以下解决方案可能是有意义的,因为除了对最后一个小的调整之外该示例程序的三行,只添加了两行:
A(rid,n) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, n FROM table_a),
B(rid,s) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, s FROM table_b)
也就是说,表A的行号增加了table_a,表B的情况类似。
不幸的是,有一个警告,尽管可能只是我没有找到相关规范的结果。但是,在深入研究之前,这里是完整的建议解决方案:
WITH
table_a (n) AS (
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
),
A(rid,n) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, n FROM table_a),
B(rid,s) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, s FROM table_b)
SELECT A.n, B.s
FROM A LEFT JOIN B
ON ( A.rid = B.rid );
已使用sqlite 3.29.0版对提议的解决方案针对各种数据集进行了测试,但是对于我来说,是否可以“保证”继续工作仍然是一个未知数。
当然,如果SQLite对UNION ALL
语句的顺序不提供任何保证(也就是说,如果问题基于错误的假设),那么有充分根据的情况将很有趣。重新配制。