MySQL: 用别名定义 CONCAT 的数据类型
MySQL: Define data type for CONCAT with alias
这个问题是关于在调用 CONCAT
并将其存储为别名时定义 capacity/max-length 的可能性。
我有一个相当复杂的 MySQL 查询,使用 Common Table Expressions (CTE) 来模拟评论。它创建一个新变量 path
,由评论的分数(投票数)和 id 组成,用逗号分隔,并使用 CONCAT
与其 parent 路径连接。这允许按分数对主题中的评论进行排序。
A path
看起来像000010,000005,000014,000008
,这意味着带有 id 8
的评论的得分为 14
而它的 parent 本身没有 parent,具有 id 5
和 10
的分数。所有具有这种格式 path
的评论都允许按我想要的方式对它们进行排序。
最重要的是,最初 path
只包含一个带有 id 的分数,在递归调用中,我们将在访问 children 时继续连接成越来越长的路径。
但是,似乎对 CONCAT
的初始调用立即将所有后续连接的大小限制为 15 到最长的初始连接,因此它们只是在 15 个字符后被剪切。使初始串联的长度超过 15,会将后续串联限制为恰好最长的初始串联(因此实际上不会串联任何东西)。
目前我已经解决了这个问题,方法是先在右边填充很多零,然后在递归调用中删除它们。但是,这使用了正则表达式,尽管它相当简单,但恐怕对性能不利。
有什么方法可以通过 CONCAT
的初始调用来定义创建的别名变量的 capacity/maximum-length 应该是什么?
这是进行的查询:
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, votes, path) AS (
(
SELECT r.id, r.content, r.parent_id, r.user_id, r.created, r.votes, CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
FROM (
SELECT c.id, c.content, c.parent_id, c.user_id, c.created, COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
) as r
)
UNION ALL
(
SELECT r.id, r.content, r.parent_id, r.user_id, r.created, r.votes, CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
FROM first_comments AS fle
JOIN (
SELECT c.id, c.content, c.parent_id, c.user_id, c.created, COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
)
SELECT id, content, parent_id, user_id, path, created, votes FROM first_comments
ORDER BY pat
(灵感来自:Order comments by thread path and by number of total votes)
最初我使用 CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
创建 path
,它创建包含 top-most 评论的分数和 ID 的路径(没有 parents),并填充 243 个零向右。所以例如000010,000005,0...0
用于 ID 为 5
的 top-most 评论。
然后递归地(但只对第一次递归调用有效,因为此后模式永远不会匹配),我们使用正则表达式删除所有尾随零,包括最后一个逗号,并添加此评论的分数和 ID:CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
.
因此,最好在 path
的初始定义中添加一些内容,而不是 work-around。但我不知道还有什么其他可能更好的方法?
感谢任何帮助和想法!
// 编辑:在 GMB 的帮助和少量补充下,问题已解决(并简化),请参阅已接受答案下的评论。
如何将路径附加到 JSON 数组而不是字符串?这无缝地克服了您遇到的问题,您仍然可以 order by
.
所以:
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, votes, js_path) AS (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes,
JSON_ARRAY(LPAD(COUNT(DISTINCT v.id), 6, 0), LPAD(c.id, 6, 0)) as js_path
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
UNION ALL
SELECT
r.id,
r.content,
r.parent_id,
r.user_id,
r.created,
r.votes,
JSON_ARRAY_APPEND(
fle.js_path,
'$', LPAD(r.votes, 6, 0),
'$', LPAD(r.id, 6, 0)
) as js_path
FROM first_comments AS fle
JOIN (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
SELECT id, content, parent_id, user_id, js_path, created, votes
FROM first_comments
ORDER BY js_path
请注意,我将查询简化如下:
递归查询的锚点中不需要子查询
union all
两个查询不需要括号
为了完成,以下是我想出的用于 MariaDB 的代码,JSON 数组的初始创建限制了它的容量,因此无法附加到它。
相反,我使用 CONCAT_WS
和 CAST
初始创建 path
到 VARCHAR(255)
,它应该足够大以包含最长的路径。
我还必须稍微更改路径中分数的计算,以便它们出现的顺序符合预期。
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, level, votes, path) AS (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
0 as level,
COUNT(DISTINCT v.id) AS votes,
CAST(CONCAT_WS(",", LPAD(999999-COUNT(DISTINCT v.id), 6, 0), LPAD(c.id, 6, 0)) AS VARCHAR(255)) as path
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
UNION ALL
SELECT
r.id,
r.content,
r.parent_id,
r.user_id,
r.created,
fle.level+1 as level,
r.votes,
CONCAT_WS(
",",
fle.js_path,
LPAD(999999-r.votes, 6, 0),
LPAD(r.id, 6, 0)
) as path
FROM first_comments AS fle
JOIN (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
SELECT id, content, parent_id, user_id, created, level, votes, path
FROM first_comments
ORDER BY path ASC
这个问题是关于在调用 CONCAT
并将其存储为别名时定义 capacity/max-length 的可能性。
我有一个相当复杂的 MySQL 查询,使用 Common Table Expressions (CTE) 来模拟评论。它创建一个新变量 path
,由评论的分数(投票数)和 id 组成,用逗号分隔,并使用 CONCAT
与其 parent 路径连接。这允许按分数对主题中的评论进行排序。
A path
看起来像000010,000005,000014,000008
,这意味着带有 id 8
的评论的得分为 14
而它的 parent 本身没有 parent,具有 id 5
和 10
的分数。所有具有这种格式 path
的评论都允许按我想要的方式对它们进行排序。
最重要的是,最初 path
只包含一个带有 id 的分数,在递归调用中,我们将在访问 children 时继续连接成越来越长的路径。
但是,似乎对 CONCAT
的初始调用立即将所有后续连接的大小限制为 15 到最长的初始连接,因此它们只是在 15 个字符后被剪切。使初始串联的长度超过 15,会将后续串联限制为恰好最长的初始串联(因此实际上不会串联任何东西)。
目前我已经解决了这个问题,方法是先在右边填充很多零,然后在递归调用中删除它们。但是,这使用了正则表达式,尽管它相当简单,但恐怕对性能不利。
有什么方法可以通过 CONCAT
的初始调用来定义创建的别名变量的 capacity/maximum-length 应该是什么?
这是进行的查询:
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, votes, path) AS (
(
SELECT r.id, r.content, r.parent_id, r.user_id, r.created, r.votes, CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
FROM (
SELECT c.id, c.content, c.parent_id, c.user_id, c.created, COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
) as r
)
UNION ALL
(
SELECT r.id, r.content, r.parent_id, r.user_id, r.created, r.votes, CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
FROM first_comments AS fle
JOIN (
SELECT c.id, c.content, c.parent_id, c.user_id, c.created, COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
)
SELECT id, content, parent_id, user_id, path, created, votes FROM first_comments
ORDER BY pat
(灵感来自:Order comments by thread path and by number of total votes)
最初我使用 CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
创建 path
,它创建包含 top-most 评论的分数和 ID 的路径(没有 parents),并填充 243 个零向右。所以例如000010,000005,0...0
用于 ID 为 5
的 top-most 评论。
然后递归地(但只对第一次递归调用有效,因为此后模式永远不会匹配),我们使用正则表达式删除所有尾随零,包括最后一个逗号,并添加此评论的分数和 ID:CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
.
因此,最好在 path
的初始定义中添加一些内容,而不是 work-around。但我不知道还有什么其他可能更好的方法?
感谢任何帮助和想法!
// 编辑:在 GMB 的帮助和少量补充下,问题已解决(并简化),请参阅已接受答案下的评论。
如何将路径附加到 JSON 数组而不是字符串?这无缝地克服了您遇到的问题,您仍然可以 order by
.
所以:
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, votes, js_path) AS (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes,
JSON_ARRAY(LPAD(COUNT(DISTINCT v.id), 6, 0), LPAD(c.id, 6, 0)) as js_path
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
UNION ALL
SELECT
r.id,
r.content,
r.parent_id,
r.user_id,
r.created,
r.votes,
JSON_ARRAY_APPEND(
fle.js_path,
'$', LPAD(r.votes, 6, 0),
'$', LPAD(r.id, 6, 0)
) as js_path
FROM first_comments AS fle
JOIN (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
SELECT id, content, parent_id, user_id, js_path, created, votes
FROM first_comments
ORDER BY js_path
请注意,我将查询简化如下:
递归查询的锚点中不需要子查询
union all
两个查询不需要括号
为了完成,以下是我想出的用于 MariaDB 的代码,JSON 数组的初始创建限制了它的容量,因此无法附加到它。
相反,我使用 CONCAT_WS
和 CAST
初始创建 path
到 VARCHAR(255)
,它应该足够大以包含最长的路径。
我还必须稍微更改路径中分数的计算,以便它们出现的顺序符合预期。
WITH RECURSIVE first_comments (id, content, parent_id, user_id, created, level, votes, path) AS (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
0 as level,
COUNT(DISTINCT v.id) AS votes,
CAST(CONCAT_WS(",", LPAD(999999-COUNT(DISTINCT v.id), 6, 0), LPAD(c.id, 6, 0)) AS VARCHAR(255)) as path
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ? AND c.parent_id IS NULL
GROUP BY c.id
UNION ALL
SELECT
r.id,
r.content,
r.parent_id,
r.user_id,
r.created,
fle.level+1 as level,
r.votes,
CONCAT_WS(
",",
fle.js_path,
LPAD(999999-r.votes, 6, 0),
LPAD(r.id, 6, 0)
) as path
FROM first_comments AS fle
JOIN (
SELECT
c.id,
c.content,
c.parent_id,
c.user_id,
c.created,
COUNT(DISTINCT v.id) AS votes
FROM comments AS c
LEFT JOIN comment_votes AS v ON c.id = v.comment_id
WHERE c.post_id = ?
GROUP BY c.id
) AS r ON fle.id = r.parent_id
)
SELECT id, content, parent_id, user_id, created, level, votes, path
FROM first_comments
ORDER BY path ASC