基于前置的SQL结果排序
我的数据库中有下表:基于前置的SQL结果排序,sql,informix,Sql,Informix,我的数据库中有下表: create table ( name varchar(25), value int, predecessor varchar(25) ); 使用示例数据: name | value | predecessor ---------------+-------+--------------- buyingredients | 10 | null eat | 3 | co
create table (
name varchar(25),
value int,
predecessor varchar(25)
);
使用示例数据:
name | value | predecessor
---------------+-------+---------------
buyingredients | 10 | null
eat | 3 | cook
cook | 12 | mixingredients
mixingredients | 5 | buyingredients
我需要一个SQL查询,该查询选择名称
和值
,排序时,前一个为null
的项目排在第一位,然后前一个等于该行名称的行是下一个,依此类推(即BuyComponents、MixComponents、cook、eat)
排序是严格线性的-如果两行具有相同的前置值,则行为是未定义的
我正在努力寻找一个SQL查询来生成我想要的排序。我真的不知道从哪里开始
我使用的是Informix数据库,不过SQL的任何变体都是一个有用的起点
更新以反映排序不是按字母顺序的这一事实,我认为您想要做的是为每个项目构造一个带有“深度”字段的临时表,然后使用该深度字段对结果进行排序。在Transact-SQL和通用表表达式中:
WITH LinkedList (name, value, predecessor, ordering)
AS
(
SELECT a.name, a.value, a.predecessor, 0
FROM YourTable a
WHERE a.predecessor IS NULL
UNION ALL
SELECT b.name, b.value, b.predecessor, c.ordering + 1
FROM YourTable b
INNER JOIN LinkedList c
ON b.predecessor = c.name
)
SELECT d.name, d.value
FROM LinkedList d
ORDER BY d.ordering, d.name
我不知道Informix是否有这种结构,但您所问的本质上是一个递归查询,在Transact-SQL中常见的表表达式为您提供了这种查询
不过,我使用的是Informix数据库
SQL的任何变体都是有用的
起点
我对Informix一无所知,所以这里有一个SQL Server版本。
@T
是我用作测试表的表变量。为了链接行,我使用递归CTE
declare @T table
(
name varchar(25),
value int,
predecessor varchar(25)
)
insert into @T
select 'buyingredients', 10, null union all
select 'cook', 12, 'mixingredients' union all
select 'mixingredients', 5, 'buyingredients' union all
select 'eat', 3, 'cook'
;with cte as
(
select T.name,
T.value,
T.predecessor,
1 as sortorder
from @T as T
where T.predecessor is null
union all
select T.name,
T.value,
T.predecessor,
C.sortorder+1 as sortorder
from @T as T
inner join cte as C
on T.predecessor = C.name
)
select C.name,
C.value,
C.predecessor
from cte as C
order by C.sortorder
您可以在这里尝试:
编辑
我仍然对Informix一无所知,但也许您可以使用Informix不支持WITH子句,更不用说递归了。这使得这些方法无法操作。没有一种特别干净的方法来做这件事。如果您有两个或三个列表,则不完全清楚应该发生什么,也不完全清楚您是在处理简单列表(示例为列表)还是更一般的树结构 但是,您可以创建一个临时表,并在存储过程中使用循环填充该表,然后按照适当的顺序从临时表中进行选择。这不是可怕的困难,而是棘手的问题。您对表进行广度优先搜索。我注意到您没有给表命名,因此在下文中称为“匿名”
CREATE TEMP TABLE Flattened
(
Hierarchy SERIAL,
Level INTEGER,
Name VARCHAR(25),
Value INTEGER,
Predecessor VARCHAR(25)
);
CREATE TEMP TABLE Intermediate
(
Hierarchy SERIAL,
Level INTEGER,
Name VARCHAR(25),
Value INTEGER,
Predecessor VARCHAR(25)
);
INSERT INTO Flattened(Hierarchy, Level, Name, Value, Predecessor)
SELECT 0, 0, name, value, predecessor
FROM Anonymous
WHERE Predecessor IS NULL;
WHILE ...any rows were inserted into table...
INSERT INTO Intermediate(Hierarchy, Level, Name, Value, Predecessor)
SELECT F.Hierarchy, F.Level + 1, A.Name, A.Value, A.Predecessor
FROM Flattened AS F, Anonymous AS A
WHERE F.Name = A.Predecessor
AND F.Level = (SELECT MAX(Level) FROM Flattened);
INSERT INTO Flattened SELECT * FROM Intermediate;
DELETE FROM Intermediate;
END WHILE
DROP TABLE Intermediate;
现在您可以使用:
SELECT Name, Value, Predecessor
FROM Flattened
ORDER BY Hierarchy, Level, Name;
唯一剩下的棘手问题是计算插入了多少行。在存储过程中,执行以下操作可能最简单:
SELECT COUNT(*) INTO n_count FROM Flattened;
及
总而言之,这对我来说是可行的(在记录的数据库中) 输出为:
buyingredients 10
mixingredients 5 buyingredients
cook 12 mixingredients
eat 3 cook
测试平台:运行在MacOS X 10.6.7上的Informix 11.70.FC2
没有使用多个独立的层次结构或树状结构层次结构而不是简单的列表进行正式测试。这不是OP想要的。@Denis:你能证明这一点吗?我在你对我自己的答案的评论中回答了这个问题(并编辑了答案)。他对订购深度不感兴趣;他对按前任订货很感兴趣,简单明了。或者他用一种非常奇怪的方式表达了这个问题。@Denis:我认为这个措辞可能有点奇怪,但我认为他的意思是,他希望顺序是绝对父项(空前置项)首先出现的顺序,然后是具有该父项(该前置项)的项的顺序,等等。@Denis深度搜索实际上是处理这个问题的一种非常合理的方法。OP想要的是类似于链表的行为——每条记录指向另一条记录。这可以通过一个深度场来实现——这会很麻烦,但会起作用。@ICR-Hi。如果您的示例给出了一个前导列不按字母顺序排列的情况,这会有所帮助,因为它现在让我们相信这就是您所追求的一切。@ICR如果记录中有数字键,您的目标会容易得多。@WillA:是的,没错,谢谢。我认为字母顺序不是理想的效果,但更新的示例将是最好的澄清方式。@Fosco:我实际上不认为如果记录有数字键,目标会更容易实现;你能解释一下吗?@Fosco我同意,如果订单是以数字形式指定的话会容易得多,但我要满足上级的突发奇想。这正是我想要的。不幸的是,我在Informix中找不到“with”结构。我会花一点时间看看是否有人可以帮助解决Informix特定的部分,但如果没有,我会接受这个答案。这并不十分清楚,但“顺序是严格线性的——如果两行具有相同的precedure值,则行为是未定义的。”这意味着它不是一棵树。
BEGIN;
CREATE TABLE Anonymous
(
Name VARCHAR(25),
Value INTEGER,
Predecessor VARCHAR(25)
);
INSERT INTO Anonymous VALUES("buyingredients", 10, NULL);
INSERT INTO Anonymous VALUES("eat", 3, "cook");
INSERT INTO Anonymous VALUES("cook", 12, "mixingredients");
INSERT INTO Anonymous VALUES("mixingredients", 5, "buyingredients");
CREATE PROCEDURE Flatten_Anonymous()
DEFINE old_count INTEGER;
DEFINE new_count INTEGER;
CREATE TEMP TABLE Flattened
(
Hierarchy SERIAL,
Level INTEGER,
Name VARCHAR(25),
Value INTEGER,
Predecessor VARCHAR(25)
);
CREATE TEMP TABLE Intermediate
(
Hierarchy SERIAL,
Level INTEGER,
Name VARCHAR(25),
Value INTEGER,
Predecessor VARCHAR(25)
);
INSERT INTO Flattened(Hierarchy, Level, Name, Value, Predecessor)
SELECT 0, 0, name, value, predecessor
FROM Anonymous
WHERE Predecessor IS NULL;
SELECT COUNT(*) INTO new_count FROM Flattened;
LET old_count = new_count - 1;
WHILE old_count != new_count
INSERT INTO Intermediate(Hierarchy, Level, Name, Value, Predecessor)
SELECT F.Hierarchy, F.Level + 1, A.Name, A.Value, A.Predecessor
FROM Flattened AS F, Anonymous AS A
WHERE F.Name = A.Predecessor
AND F.Level = (SELECT MAX(Level) FROM Flattened);
INSERT INTO Flattened SELECT * FROM Intermediate;
DELETE FROM Intermediate;
LET old_count = new_count;
SELECT COUNT(*) INTO new_count FROM Flattened;
END WHILE
DROP TABLE Intermediate;
END PROCEDURE;
EXECUTE PROCEDURE Flatten_Anonymous();
SELECT Name, Value, Predecessor
FROM Flattened
ORDER BY Hierarchy, Level, Name;
DROP TABLE Flattened;
ROLLBACK;
buyingredients 10
mixingredients 5 buyingredients
cook 12 mixingredients
eat 3 cook