- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 SQL Server 中
我有一个数据库,其中有一个名为 Recipe 的表,该表包含食谱和食谱中的 Material 。我需要一个查询来显示配方的所有基础 Material 。不幸的是,其中一些 Material 实际上是食谱中的其他食谱。例如:您有配方 AA01,其中包含 2 种基础 Material 和配方 BB01。您现在必须查找该配方中的 Material 以找到 AA01 中的基础 Material 。诀窍是,您可以在食谱中拥有无限量的食谱。这可以是一个搜索的阶梯。你不知道你必须向下看多远的阶梯。
我想出了一个搜索和查找下一个 Material 并循环直到最终找到基础 Material 的查询的想法。有时它可能必须循环一次才能找到 Material ,有时它可能必须循环 5 次才能下 5 个级别。
不幸的是,下面的代码无法循环,所以它只能找到第一层。我无法使查询循环自身。
SELECT
Recipe.RecipeID,
Recipe_1.RecipeID,
Recipe_1.MaterialID
FROM Recipe
LEFT JOIN Recipe AS Recipe_1 ON Recipe.MaterialID = Recipe_1.RecipeID
ORDER BY Recipe.RecipeID;
解决方案是将这段代码放入一个循环中,或者让它递归地调用自己,直到它达到所有基础 Material 都已找到的级别。我附上了一张 RECIPE 表示例的图片,代码生成的内容,以及我需要它无限生成(更多级别)的内容。您可以看到突出显示的行是实际行中缺少的行,这些 Material 需要 2 个级别才能最终成为基础 Material 。我已将其硬编码为最多搜索 5 层,但显然可以有更多层。我怎样才能有 SQL 循环并自己分配层?
是否可以创建一个循环来不断循环查询自身?
最佳答案
这看起来正在产生您正在寻找的结果...
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#Recipe', 'U') IS NOT NULL
DROP TABLE #Recipe;
CREATE TABLE #Recipe (
Recipe VARCHAR(5) NOT NULL,
Material VARCHAR(5) NOT NULL
);
INSERT #Recipe (Recipe, Material) VALUES
('aa01', 'B1'),
('aa01', 'B2'),
('aa01', 'bb01'),
('bb01', 'B1'),
('bb01', 'cc01'),
('cc01', 'B3'),
('cc01', 'B4'),
('B1', 'B1'),
('B2', 'B2'),
('B3', 'B3'),
('B4', 'B4');
--SELECT * FROM #Recipe r;
--====================================================================================
IF OBJECT_ID('tempdb..#RecursiveOutput', 'U') IS NOT NULL
DROP TABLE #RecursiveOutput;
WITH
cte_Recursion AS (
SELECT
r.Recipe,
x = r.Material,
Material = CAST(r.Material AS VARCHAR(8000)),
NodeLevel = 1,
MaterialLevel = CAST('m1.Material' AS VARCHAR(8000))
FROM
#Recipe r
UNION ALL
SELECT
cr.Recipe,
x = r.Material,
Material = CAST(CONCAT(cr.Material, '>', r.Material) AS VARCHAR(8000)),
NodeLevel = cr.NodeLevel + 1,
MaterialLevel = CAST(CONCAT('m', cr.NodeLevel + 1, '.Material, ', cr.MaterialLevel) AS VARCHAR(8000))
FROM
cte_Recursion cr
JOIN #Recipe r
ON cr.x = r.Recipe
WHERE 1 = 1
AND cr.Recipe <> r.Recipe
AND r.Recipe <> r.Material
)
SELECT
cr.Recipe,
cr.Material,
cr.NodeLevel,
cr.MaterialLevel
INTO #RecursiveOutput
FROM
cte_Recursion cr;
-------------------------------------
DECLARE
@Split VARCHAR(8000) = '',
@Material VARCHAR(8000) = '',
@Level VARCHAR(8000) = '',
@SelectList VARCHAR(8000) = '',
@MaxNode INT = 0,
@DeBug BIT = 0; -- set to 0 to execute & set to 1 to print...
SELECT
@Split = CONCAT(@Split, '
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(''>'', ro.Material, ', CASE WHEN ro.NodeLevel = 1 THEN '1' ELSE CONCAT('s', ro.NodeLevel - 1, '.Split + 1') END, '), 0)) ) s', ro.NodeLevel, ' (Split)'),
@Material = CONCAT(@Material, '
CROSS APPLY ( VALUES (SUBSTRING(ro.Material, ', CASE WHEN ro.NodeLevel = 1 THEN '1, ISNULL(s1.Split -1,' ELSE CONCAT('s', ro.NodeLevel - 1, '.Split + 1, ISNULL(s', ro.NodeLevel, '.Split - s', ro.NodeLevel - 1, '.Split - 1,') END, ' 1000))) ) m', ro.NodeLevel, ' (Material)'),
@Level = CONCAT(@Level, CASE WHEN ro.NodeLevel = 1 THEN '' ELSE CONCAT('
CROSS APPLY ( VALUES (CAST(COALESCE(', ro.MaterialLevel, ') AS VARCHAR(20))) ) L', ro.NodeLevel, ' ([Level ', ro.NodeLevel, ' Material])') END),
@SelectList = CONCAT(@SelectList, CASE WHEN ro.NodeLevel = 1 THEN '' ELSE CONCAT(',
L', ro.NodeLevel, '.[Level ', ro.NodeLevel, ' Material]') END),
@MaxNode = CASE WHEN ro.NodeLevel > @MaxNode THEN ro.NodeLevel ELSE @MaxNode END
FROM
#RecursiveOutput ro
GROUP BY
ro.NodeLevel,
ro.MaterialLevel;
DECLARE @sql VARCHAR(MAX) = CONCAT('
SELECT DISTINCT
ro.Recipe,
[Level 1 Material] = CAST(m1.Material AS VARCHAR(20))',
@SelectList, '
FROM
#RecursiveOutput ro',
@Split,
@Material,
@Level, '
WHERE
EXISTS (SELECT 1 FROM #Recipe r WHERE L', @MaxNode, '.[Level ', @MaxNode, ' Material] = r.Recipe AND r.Recipe = r.Material);')
IF @DeBug = 1
BEGIN
PRINT(@sql);
END;
ELSE
BEGIN
EXEC(@sql);
END;
结果...
Recipe Level 1 Material Level 2 Material Level 3 Material
------ -------------------- -------------------- --------------------
aa01 B1 B1 B1
aa01 B2 B2 B2
aa01 bb01 B1 B1
aa01 bb01 cc01 B3
aa01 bb01 cc01 B4
B1 B1 B1 B1
B2 B2 B2 B2
B3 B3 B3 B3
B4 B4 B4 B4
bb01 B1 B1 B1
bb01 cc01 B3 B3
bb01 cc01 B4 B4
cc01 B3 B3 B3
cc01 B4 B4 B4
编辑:下面是与上面相同的解决方案,但编写是为了消除对早期版本的 SQL Server 的 CONCAT 函数的使用...
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#Recipe', 'U') IS NOT NULL
DROP TABLE #Recipe;
CREATE TABLE #Recipe (
Recipe VARCHAR(5) NOT NULL,
Material VARCHAR(5) NOT NULL
);
INSERT #Recipe (Recipe, Material) VALUES
('aa01', 'B1'),
('aa01', 'B2'),
('aa01', 'bb01'),
('bb01', 'B1'),
('bb01', 'cc01'),
('cc01', 'B3'),
('cc01', 'B4'),
('B1', 'B1'),
('B2', 'B2'),
('B3', 'B3'),
('B4', 'B4');
--SELECT * FROM #Recipe r;
--====================================================================================
IF OBJECT_ID('tempdb..#RecursiveOutput', 'U') IS NOT NULL
DROP TABLE #RecursiveOutput;
WITH
cte_Recursion AS (
SELECT
r.Recipe,
x = r.Material,
Material = CAST(r.Material AS VARCHAR(8000)),
NodeLevel = 1,
MaterialLevel = CAST('m1.Material' AS VARCHAR(8000))
FROM
#Recipe r
UNION ALL
SELECT
cr.Recipe,
x = r.Material,
Material = CAST(cr.Material + '>' + r.Material AS VARCHAR(8000)),
NodeLevel = cr.NodeLevel + 1,
MaterialLevel = CAST('m' + CAST(cr.NodeLevel + 1 AS VARCHAR(10)) + '.Material, ' + cr.MaterialLevel AS VARCHAR(8000))
FROM
cte_Recursion cr
JOIN #Recipe r
ON cr.x = r.Recipe
WHERE 1 = 1
AND cr.Recipe <> r.Recipe
AND r.Recipe <> r.Material
)
SELECT
cr.Recipe,
cr.Material,
cr.NodeLevel,
cr.MaterialLevel
INTO #RecursiveOutput
FROM
cte_Recursion cr;
-------------------------------------
DECLARE
@Split VARCHAR(8000) = '',
@Material VARCHAR(8000) = '',
@Level VARCHAR(8000) = '',
@SelectList VARCHAR(8000) = '',
@MaxNode INT = 0,
@DeBug BIT = 0; -- set to 0 to execute & set to 1 to print...
SELECT
@Split = @Split + '
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(''>'', ro.Material, ' + CASE WHEN ro.NodeLevel = 1 THEN '1' ELSE 's' + CAST(ro.NodeLevel - 1 AS VARCHAR(10)) + '.Split + 1' END + '), 0)) ) s' + CAST(ro.NodeLevel AS VARCHAR(10)) + ' (Split)',
@Material = @Material + '
CROSS APPLY ( VALUES (SUBSTRING(ro.Material, ' + CASE WHEN ro.NodeLevel = 1 THEN '1, ISNULL(s1.Split -1,' ELSE 's' + CAST(ro.NodeLevel - 1 AS VARCHAR(10)) + '.Split + 1, ISNULL(s' + CAST(ro.NodeLevel AS VARCHAR(10)) + '.Split - s'
+ CAST(ro.NodeLevel - 1 AS VARCHAR(10)) + '.Split - 1,' END + ' 1000))) ) m' + CAST(ro.NodeLevel as VARCHAR(10)) + ' (Material)',
@Level = @Level + CASE WHEN ro.NodeLevel = 1 THEN '' ELSE '
CROSS APPLY ( VALUES (CAST(COALESCE(' + ro.MaterialLevel + ') AS VARCHAR(20))) ) L' + CAST(ro.NodeLevel AS VARCHAR(10)) + ' ([Level ' + CAST(ro.NodeLevel as VARCHAR(10)) + ' Material])' END,
@SelectList = @SelectList + CASE WHEN CAST(ro.NodeLevel as VARCHAR(10)) = 1 THEN '' ELSE ',
L' + CAST(ro.NodeLevel AS VARCHAR(10)) + '.[Level ' + CAST(ro.NodeLevel AS VARCHAR(10)) + ' Material]' END,
@MaxNode = CASE WHEN ro.NodeLevel > @MaxNode THEN ro.NodeLevel ELSE @MaxNode END
FROM
#RecursiveOutput ro
GROUP BY
ro.NodeLevel,
ro.MaterialLevel;
DECLARE @sql VARCHAR(MAX) = '
SELECT DISTINCT
ro.Recipe,
[Level 1 Material] = CAST(m1.Material AS VARCHAR(20))' +
@SelectList + '
FROM
#RecursiveOutput ro' +
@Split +
@Material +
@Level + '
WHERE
EXISTS (SELECT 1 FROM #Recipe r WHERE L' + CAST(@MaxNode AS VARCHAR(10)) + '.[Level ' + CAST(@MaxNode AS VARCHAR(10)) + ' Material] = r.Recipe AND r.Recipe = r.Material);'
IF @DeBug = 1
BEGIN
PRINT(@sql);
END;
ELSE
BEGIN
EXEC(@sql);
END;
喂,杰森
关于sql - 如何无限查询查询,直到在 SQL Server 中达到条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45744039/
我已经下载了 RStudio,在打开我的代码所在的文件时,我似乎已经达到了容量限制: The file is 2.3MB the maximum file size is 2MB The file i
我有一个按钮,每次单击时,都会将 1 添加到变量中。当此变量超过 5 时,将触发警报。然而,此后触发器仍不断激活。我尝试使用 == 而不是 > 进行检查,但它做同样的事情。有什么想法吗? http:/
我正在将Slick 3.0与HikariCP 2.3.8一起使用(也可以玩2.4) 我做了很多数据库IO,并且不断达到队列限制。 有没有一种方法可以获取当前的队列大小,以及如何增加队列大小? 还是建议
在 Salesforce 中,您可以设置各种工作流程或构建用于发送电子邮件的 API 应用程序。对于大多数标准 Salesforce 组织,每天有 1000 封电子邮件的限制。 (例如,参见 here
我有一个类是这样的: public sealed class Contract { public bool isExpired { get; set; } public DateTim
我有一个带有特殊符号按钮的输入作为附加组件。 HTML
我正在尝试压缩 pdf 文件(有时是图像)。我需要一个 java 压缩器来帮助我压缩文件。我需要尺寸小于原始文档尺寸的一半。我尝试了java api中给出的deflator。但它并不是很成功。请帮我解
我正在使用这条线来创建淡入效果。 $('#div').css({opacity: 0, visibility:"visible"}).animate({opacity: 1}, 500); 可见类达到
我使用 URLCache 来缓存请求响应,最大容量如下: let diskCapacity = 100 * 1024 * 1024 let memoryCapacity = 100
我有一个计数器函数,我从这个 Answer 得到它: function countDown(i) { var int = setInterval(function () {
下面是一段代码,用于检查给定数字是否为 Lychrel 数字。这基本上意味着该程序取一个数及其倒数之和,然后取那个数及其倒数之和,等等,直到找到回文。如果它在一定的迭代次数内没有找到这样的数字(我在这
我即将对这个可怕的旧 Java Web 应用程序做一些工作,这是我的一个 friend 不久前继承的。 在我设置 tomcat、导入项目和所有这些到我的 eclipse 工作区后,我收到此错误,指出
我有一个 NSDictionary 对象,其中包含深层结构,例如包含包含字典的进一步数组的数组... 我想在层次结构中向下获取一个对象。是否有任何直接索引方法可以使用键名或其他方式获取它们? 多次调用
正如标题所说,我的 .border div 的边框跨度比它里面的要宽。它只会在达到 710px 时发生,因此您需要在 this fiddle 中展开结果窗口。 . 我希望边框保持在其内容周围而不超过它
我在 MySQL 中有一个表,通过 Microsoft Access 2013 中的链接表(通过 ODBC) Access 。 此表包含超过 124,000 条记录,我需要一个表单中的 ComboBo
一旦上一个输入达到其最大长度值,我如何才能聚焦下一个输入? a: b: c: 如果用户粘贴的文本大于最大长度,理想情况下它应该溢出到下一个输入。 jsFiddle: http://jsfiddl
我的任务是在客户的 QA 服务器上提供服务器性能报告。理想情况下,客户希望对约 900 个并发用户进行负载测试,因为这是他们在高峰时段通常使用的数量。然而,我一直在做的负载测试正在使他们的 QA 服务
我在 django 应用程序中对我的 celery worker 运行任务,其中每个任务执行大约需要 1-2 秒。通常这些执行都很好,但有时,特别是如果 Django 应用程序已经部署了一段时间,我开
我有一个 one_for_one 主管来处理类似且完全独立的 child 。 当一个 child 出现问题时,反复崩溃并触发: =SUPERVISOR REPORT==== 30-Mar-2011::
根据该网站,他们在免费计划中限制了 100 个并发连接,但是当第 101 个连接尝试连接时,它被拒绝,那么什么时候允许新连接? 例如:用户是否必须等待一定时间或一旦一个连接关闭,另一个连接就有机会连接
我是一名优秀的程序员,十分优秀!