[英]MySQL: Define data type for CONCAT with alias
This question is about the possibility to define a capacity/max-length when calling CONCAT
and storing it as an alias.这个问题是关于在调用
CONCAT
并将其存储为别名时定义容量/最大长度的可能性。
I have a rather complex MySQL query using Common Table Expressions (CTE) used to model comments.我有一个相当复杂的 MySQL 查询,它使用用于 model 评论的公用表表达式(CTE)。 It creates a new variable
path
consisting of a comment's score (# of votes) and id seperated by a comma and concatenated with its parent path using CONCAT
.它创建了一个新的变量
path
,由评论的分数(投票数)和 id 组成,以逗号分隔,并使用CONCAT
与其父路径连接。 This allow sorting comments within threads by their score.这允许按分数对线程内的评论进行排序。
A path
looks like eg 000010,000005,000014,000008
, which means that the comment with id 8
has a score of 14
and its parent, which itself does not have a parent, has id 5
and a score of 10
. path
看起来像例如000010,000005,000014,000008
,这意味着 id 为8
的评论的得分为14
,其父级(本身没有父级)的 id 为5
且得分为10
。 With all comments having path
of this format allows sorting them how I want.所有评论都具有这种格式的
path
,允许按照我想要的方式对它们进行排序。
The bottom line is that initially the path
only consists of a single score with id, and in the recursive call we will continue concatenating into longer and longer paths when visiting the children.底线是最初的
path
只包含一个带有 id 的分数,并且在递归调用中,我们将在访问孩子时继续连接成越来越长的路径。
However, it seems that the initial call to CONCAT
immediately limits the size to 15 of all subsequent concatenations to the longest initial concatenation, so they are just cut after 15 characters.但是,对
CONCAT
的初始调用似乎立即将所有后续串联的大小限制为 15 个最长的初始串联,因此它们仅在 15 个字符后被剪切。 Making the initial concatenation longer than 15, will limit the subsequent concatenations to exactly the longest initial concatenation (so effectively doesn't concatenate anything).使初始连接长于 15,会将后续连接限制为恰好最长的初始连接(因此实际上不会连接任何东西)。
Currently I have worked around this issue by initially padding a lot of zeros to the right and remove them in the recursive call.目前我已经通过最初在右侧填充很多零并在递归调用中删除它们来解决这个问题。 However, this uses regular expression and even though it is fairly simply I am afraid it is not good for performance.
但是,这使用了正则表达式,尽管它相当简单,但恐怕它对性能不利。
Is there any way to define with the initial call of CONCAT
what the capacity/maximum-length of the created alias variable should be?有什么方法可以通过
CONCAT
的初始调用来定义创建的别名变量的容量/最大长度应该是多少?
This is the query that is made:这是进行的查询:
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
(Inspired by: Order comments by thread path and by number of total votes ) (灵感来源: 按线程路径和总票数排序评论)
Initially I create path
with CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
, which creates the path containing the score and id of top-most comments (without parents), and pads 243 zeros to the right.最初我使用
path
CONCAT_WS(",", LPAD(r.votes,6,0), LPAD(r.id,6,0), LPAD(0,243,0)) as path
创建路径,它创建包含分数的路径和最顶部评论的 id(没有父母),并在右侧填充 243 个零。 So eg 000010,000005,0...0
for the top-most comment with id 5
.因此,例如
000010,000005,0...0
用于 id 为5
的最高评论。
Then recursively (but effectively only with the first recursive call, as thereafter the pattern never matches), we use regular expressions to remove all the trailing zeros including the last comma and add the score and id of this comment: CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
.然后递归(但仅在第一次递归调用时有效,因为此后模式永远不会匹配),我们使用正则表达式删除所有尾随零,包括最后一个逗号,并添加此注释的分数和 id:
CONCAT_WS(",", REGEXP_REPLACE(fle.path, ",[0]+$", ""), LPAD(r.votes,6,0), LPAD(r.id,6,0)) as path
。
It would therefore be nice to just add something to the initial definition of path
instead of this work-around.因此,最好在
path
的初始定义中添加一些东西而不是这种解决方法。 But I don't know what other way could be possible and better?但我不知道还有什么其他方法可能更好?
Any help and idea is appreciated!任何帮助和想法表示赞赏!
// Edit: Problem was solved (and simplified) with GMB's help and a small addition, see my comment under the accepted answer. // 编辑:在 GMB 的帮助和少量补充下,问题已解决(并简化),请参阅我在接受的答案下的评论。
How about appending the paths into a JSON array instead of a string?如何将路径附加到 JSON 数组而不是字符串? This seamlessly overcomes the problem that you are having, and you can still
order by
.这无缝地解决了您遇到的问题,您仍然可以
order by
。
So:所以:
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
Note that I simplified the query as follows:请注意,我将查询简化如下:
there is no need for a subquery in the anchor of the recursive query递归查询的锚点中不需要子查询
union all
does not require parentheses around the two queries union all
不需要在两个查询周围加上括号
Just for completion, the following is the code I came up with to use with MariaDB, for which the initial creation of the JSON array limits its capacity making it impossible to append to it.只是为了完成,以下是我想出的与 MariaDB 一起使用的代码,其中 JSON 数组的初始创建限制了它的容量,使其无法使用 append。
Instead I use CONCAT_WS
and CAST
the initial creation of path
to a VARCHAR(255)
, which should be large enough to contain the longest paths.相反,我使用
CONCAT_WS
和CAST
初始创建到VARCHAR(255)
的path
,它应该足够大以包含最长的路径。
I also had to change the calculation of the score in the path slightly, so that the order they appear is as expected.我还必须稍微更改路径中分数的计算,以便它们出现的顺序符合预期。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.