我正在尝试在SQL Server中创建分层查询。
我有4张桌子:
Org3
是最高级别,而org 6是最低级别。如您所见,org6在org5表中具有一个父ID,在org4表中具有一个父ID,在org3表中具有一个父ID。
父母可能没有孩子。
所以说我在表中有这些值:
Org3 表
| org3_id | org3_name |
+---------+-----------+
| 1 | MS |
| 2 | NS |
Org4 表
| org4_id | org4_name | org4_org3id |
+---------+-----------+-------------+
| 1 | TS | 1 |
| 2 | QS | 1 |
| 3 | BS | 1 |
Org5 表
| org5_id | org5_name | org5_org4id |
+---------+-----------+-------------+
| 1 | LS | 1 |
| 2 | PS | 1 |
| 3 | VS | 2 |
Org6 表
| org6_id | org6_name | org6_org5id |
+---------+-----------+-------------+
| 1 | AS | 1 |
| 2 | RS | 1 |
| 3 | ZS | 2 |
我想要得到的结果是:
| org3_id | org3_name | org4_id | org4_name | org5_id | org5_name | org6_id | org6_name | path |
|---------|-----------|---------|-----------|---------|-----------|---------|-----------|-------------|
| 1 | MS | NULL | NULL | NULL | NULL | NULL | NULL | MS |
| 1 | MS | 1 | TS | NULL | NULL | NULL | NULL | MS\TS |
| 1 | MS | 1 | TS | 1 | LS | NULL | NULL | MS\TS\LS |
| 1 | MS | 1 | TS | 1 | LS | 1 | AS | MS\TS\LS\AS |
| 1 | MS | 1 | TS | 1 | LS | 2 | RS | MS\TS\LS\RS |
| 1 | MS | 1 | TS | 2 | PS | NULL | NULL | MS\TS\PS |
| 1 | MS | 1 | TS | 2 | PS | 3 | ZS | MS\TS\PS\ZS |
| 1 | MS | 2 | QS | NULL | NULL | NULL | NULL | MS\QS |
| 1 | MS | 2 | QS | 3 | VS | NULL | NULL | MS\QS\VS |
| 1 | MS | 3 | BS | NULL | NULL | NULL | NULL | MS\BS |
| 2 | NS | NULL | NULL | NULL | NULL | NULL | NULL | NS |
这是我尝试过的。
SELECT
org3.org3_id,
org3.org3_name,
org3.org3_open_ind,
org4.org4_id,
org4.org4_name,
org4.org4_open_ind,
org5.org5_id,
org5.org5_name,
org5.org5_open_ind,
org6.org6_id,
org6.org6_name,
org6.org6_open_ind,
CONCAT(org3.org3_abbrv, '\', org4.org4_abbrv,
CASE
WHEN org5.org5_abbrv IS NULL THEN ''
ELSE CONCAT('\', org5.org5_abbrv)
END,
CASE
WHEN org6.org6_abbrv IS NULL THEN ''
ELSE CONCAT('\', org6.org6_abbrv)
END) AS [ORG PATH]
FROM
(SELECT
*
FROM
TSTAFFORG3
WHERE
org3_open_ind = 1) org3
LEFT OUTER JOIN
(SELECT
*
FROM
TSTAFFORG4
WHERE
org4_open_ind = 1) org4 ON org4.org4_org3id = org3.org3_id
LEFT OUTER JOIN
(SELECT
*
FROM
TSTAFFORG5
WHERE
org5_open_ind = 1) org5 ON org5.org5_org4id = org4.org4_id
LEFT OUTER JOIN
(SELECT
*
FROM
TSTAFFORG6
WHERE
org6_open_ind = 1) org6 ON org6.org6_org5id = org5.org5_id
ORDER BY
org3.org3_name, org4.org4_name, org5.org5_name, org6.org6_name
我认为也许需要CTE查询,但是在这种情况下我不确定如何对其进行构架。如果所有表都在一个表中,我想我可以弄清楚,但是由于它是多个表,所以我很难弄清SQL。我尝试的查询不只显示父级。它只会显示org3有子级的结果。
答案 0 :(得分:2)
这是一个可能的解决方案,但我不是它会如何执行。基本上是在所有级别上添加一个空行。
SELECT
org3.org3_id,
org3.org3_name,
org3.org3_open_ind,
org4.org4_id,
org4.org4_name,
org4.org4_open_ind,
org5.org5_id,
org5.org5_name,
org5.org5_open_ind,
org6.org6_id,
org6.org6_name,
org6.org6_open_ind,
CONCAT
(
org3.org3_name,
'\' + org4.org4_name,
'\' + org5.org5_name,
'\' + org6.org6_name
) AS [ORG PATH]
FROM TSTAFFORG3 org3
CROSS APPLY
(
SELECT org4_id, org4_name, org4_open_ind, org4_org3id
FROM TSTAFFORG4
WHERE org4_open_ind = 1
AND org4_org3id = org3.org3_id
UNION ALL
SELECT NULL, NULL, NULL, org3.org3_id
) org4
CROSS APPLY
(
SELECT org5_id, org5_name, org5_open_ind, org5_org4id
FROM TSTAFFORG5
WHERE org5_open_ind = 1
AND org5_org4id = org4.org4_id
UNION ALL
SELECT NULL, NULL, NULL, org4.org4_id
) org5
OUTER APPLY
(
SELECT org6_id, org6_name, org6_open_ind, org6_org5id
FROM TSTAFFORG6
WHERE org6_open_ind = 1
AND org6_org5id = org5.org5_id
UNION ALL
SELECT NULL, NULL, NULL, org4.org4_id
) org6
WHERE
org3_open_ind = 1
ORDER BY
org3.org3_name, org4.org4_name, org5.org5_name, org6.org6_name;
如果其他人有不同的想法,我将样本数据保留为可消耗格式。
CREATE TABLE TSTAFFORG3(
org3_id int,
org3_name varchar(10),
org3_open_ind bit);
INSERT INTO TSTAFFORG3
VALUES
( 1, 'MS', 1),
( 2, 'NS', 1);
CREATE TABLE TSTAFFORG4(
org4_id int,
org4_name varchar(10),
org4_org3id int,
org4_open_ind bit);
INSERT INTO TSTAFFORG4
VALUES
( 1, 'TS', 1, 1),
( 2, 'QS', 1, 1),
( 3, 'BS', 1, 1);
CREATE TABLE TSTAFFORG5(
org5_id int,
org5_name varchar(10),
org5_org4id int,
org5_open_ind bit);
INSERT INTO TSTAFFORG5
VALUES
( 1, 'LS', 1, 1),
( 2, 'PS', 1, 1),
( 3, 'VS', 2, 1);
CREATE TABLE TSTAFFORG6(
org6_id int,
org6_name varchar(10),
org6_org5id int,
org6_open_ind bit);
INSERT INTO TSTAFFORG6
VALUES
( 1, 'AS', 1, 1),
( 2, 'RS', 1, 1),
( 3, 'ZS', 2, 1);
答案 1 :(得分:1)
要清楚:您正在对分层数据建模。在RDBMS中有多种存储分层数据的方法。两个是:
TS/MS/RS
)。您的数据模型似乎有问题:如果要添加另一个级别,是否要添加新表?
如果可以的话,应将所有内容移到一张表中:
orgs(org_id, org_name, parent_org)
这使用邻接列表方法。
然后,您可以创建一个简单的递归CTE(或多个自联接)来获得具体的路径。
避开组织ID(如果将所有内容放在一张表中则必须重新生成),以下查询将为您提供以下结果:
WITH -- sample data (I'm only using org names, not org IDs). Orgs(org_name, parent_org) AS ( SELECT * FROM ( VALUES ('MS', NULL), ('NS', NULL), ('TS', 'MS'), ('QS', 'MS'), ('BS', 'MS'), ('LS', 'TS'), ('PS', 'TS'), ('VS', 'QS'), ('AS', 'LS'), ('RS', 'LS'), ('ZS', 'PS') ) v(c1, c2) ), -- hierarchical/recursive CTE OrgsWithPath(org_name, parent_org, org_path) AS ( SELECT org_name, parent_org, CAST(org_name AS VARCHAR(MAX)) FROM Orgs WHERE parent_org IS NULL UNION ALL SELECT Orgs.org_name, Orgs.parent_org, OrgsWithPath.org_path + '\' + Orgs.org_name FROM OrgsWithPath INNER JOIN Orgs ON Orgs.parent_org = OrgsWithPath.org_name ) SELECT * FROM OrgsWithPath ORDER BY org_path
+----------+------------+-------------+ | org_name | parent_org | org_path | +----------+------------+-------------+ | MS | NULL | MS | | BS | MS | MS\BS | | QS | MS | MS\QS | | VS | QS | MS\QS\VS | | TS | MS | MS\TS | | LS | TS | MS\TS\LS | | AS | LS | MS\TS\LS\AS | | RS | LS | MS\TS\LS\RS | | PS | TS | MS\TS\PS | | ZS | PS | MS\TS\PS\ZS | | NS | NULL | NS | +----------+------------+-------------+
请注意最后一个ORDER BY
中的SELECT
:这确定您的查询是深度优先(遍历完整路径)还是广度优先(从所有顶级节点开始,然后进行)。通过这种方法,包含一个“级别”也很容易,因此您知道它是顶级节点还是其他级别。
获取其他列比较麻烦,但也可以由递归CTE处理(使用CASE
和COALESCE
):
WITH OrgsWithPath(org_name, org_path, org_level, org3_name, org4_name, org5_name, org6_name) AS ( SELECT Orgs.org_name, CAST(org_name AS VARCHAR(MAX)), 1, Orgs.org_name, CAST(NULL AS VARCHAR(255)), CAST(NULL AS VARCHAR(255)), CAST(NULL AS VARCHAR(255)) FROM Orgs WHERE parent_org IS NULL UNION ALL SELECT Orgs.org_name, OrgsWithPath.org_path + '\' + Orgs.org_name, OrgsWithPath.org_level + 1, OrgsWithPath.org3_name, CASE WHEN OrgsWithPath.org_level+1 >= 2 THEN COALESCE(OrgsWithPath.org4_name, Orgs.org_name) END, CASE WHEN OrgsWithPath.org_level+1 >= 3 THEN COALESCE(OrgsWithPath.org5_name, Orgs.org_name) END, CASE WHEN OrgsWithPath.org_level+1 >= 4 THEN COALESCE(OrgsWithPath.org6_name, Orgs.org_name) END FROM OrgsWithPath INNER JOIN Orgs ON Orgs.parent_org = OrgsWithPath.org_name ) SELECT * FROM OrgsWithPath ORDER BY org_path
+----------+-------------+-----------+-----------+-----------+-----------+-----------+ | org_name | org_path | org_level | org3_name | org4_name | org5_name | org6_name | +----------+-------------+-----------+-----------+-----------+-----------+-----------+ | MS | MS | 1 | MS | NULL | NULL | NULL | | BS | MS\BS | 2 | MS | BS | NULL | NULL | | QS | MS\QS | 2 | MS | QS | NULL | NULL | | VS | MS\QS\VS | 3 | MS | QS | VS | NULL | | TS | MS\TS | 2 | MS | TS | NULL | NULL | | LS | MS\TS\LS | 3 | MS | TS | LS | NULL | | AS | MS\TS\LS\AS | 4 | MS | TS | LS | AS | | RS | MS\TS\LS\RS | 4 | MS | TS | LS | RS | | PS | MS\TS\PS | 3 | MS | TS | PS | NULL | | ZS | MS\TS\PS\ZS | 4 | MS | TS | PS | ZS | | NS | NS | 1 | NS | NULL | NULL | NULL | +----------+-------------+-----------+-----------+-----------+-----------+-----------+