【问题标题】:Using a CTE to split results across a CROSS APPLY使用 CTE 在 CROSS APPLY 中拆分结果
【发布时间】:2017-07-07 10:31:28
【问题描述】:

我有一些数据需要输出为包含标记标签的行,我在表值函数中执行此操作。

使用以下格式的代码,使用search 查询收集我的数据,然后使用results 的输出插入到我返回的表中,这一直运行良好。

我现在需要获取更长的数据字段并将其拆分为多行,而我不知道如何实现这一点。

我的想法是,我想使用 CTE 来处理来自我的查询的数据,但我看不到将数据从我的 search 查询获取到我的 CTE 并从那里获取到我的 @ 987654324@设置。

我想我可以通过在数据库中创建另一个表值函数来看到另一种方法,如果我将其提供给我的comment_text 列,该函数将返回结果集,但这样做似乎很浪费。

有人看到解决方案的路径吗?

“真实”表示例:

DECLARE @Comments TABLE
(
    id INT NOT NULL IDENTITY PRIMARY KEY CLUSTERED,
    comment_date DATETIME NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    comment_title VARCHAR(50) NOT NULL,
    comment_text char(500)
);

添加评论行:

INSERT INTO @Comments VALUES(CURRENT_TIMESTAMP, 'Bob', 'Example','Bob''s Comment', 'Text of Bob''s comment.');
INSERT INTO @Comments VALUES(CURRENT_TIMESTAMP, 'Alice', 'Example','Alice''s Comment', 'Text of Alice''s comment that is much longer and will need to be split over multiple rows.');

返回结果表格式:

DECLARE @return_table TABLE
(
   comment_date DATETIME,
   commenter_name VARCHAR(101),
   markup VARCHAR(100)
);

朴素查询(无法运行,因为SplitComment CTE中的变量comment_text无法识别。

WITH SplitComment(note,start_idx) AS
(
   SELECT '<Note>'+SUBSTRING(comment_text,0,50)+'</Note>', 0
   UNION ALL
   SELECT '<Text>'+SUBSTRING(note,start_idx,50)+'</Text>', start_idx+50 FROM SplitComment WHERE (start_idx+50) < LEN(note)
)
INSERT INTO @return_table
SELECT results.* FROM
(
   SELECT
   comment_date,
   CAST(first_name+' '+last_name AS VARCHAR(101)) commenter,
   comment_title,
   comment_text
   FROM @Comments
) AS search
CROSS APPLY
(
           SELECT comment_date, commenter, '<title>'+comment_title+'</title>' markup
 UNION ALL SELECT comment_date, commenter, SplitComment
) AS results;

SELECT * FROM @return_table;

结果(当函数在没有 CTE 的情况下运行时):

comment_date            commenter_name                                                                                        markup
2017-07-07 11:53:57.240 Bob Example                                                                                           <title>Bob's Comment</title>
2017-07-07 11:53:57.240 Alice Example                                                                                         <title>Alice's Comment</title>

理想情况下,我希望为 Bob 的评论增加一行,为 Alice 的评论增加两行。像这样的:

comment_date            commenter_name   markup
2017-07-07 11:53:57.240 Bob Example      <title>Bob's Comment</title>
2017-07-07 11:53:57.240 Bob Example      <Note>Bob's Comment</Note>
2017-07-07 11:53:57.240 Alice Example    <title>Alice's Comment</title>
2017-07-07 11:53:57.240 Alice Example    <Note>Text of Alice''s comment that is much longer and w</Note>
2017-07-07 11:53:57.240 Alice Example    <Text>ill need to be split over multiple rows.</Text>

【问题讨论】:

  • 有任何样品说明您正在放入什么以及您希望看到什么?
  • 如果你想从 T-SQL 生成 XML/HTML,请使用 FOR XML 而不是字符串连接。
  • @Bogdan_Sahlean 我不想生成正确的 XML/HTML,我需要以这种疯狂的格式返回结果,以便它们可以被业务中其他地方使用的另一个应用程序使用。你还没有见过这个功能最糟糕的东西......
  • 您真的需要交叉申请吗?如果日期是评论和评论者之间的联系 - 它不只是在您的 SplitComment 中分配一个行号,并且内部/左加入您的评论者,评论和排序?
  • @AllanS.Hansen 我的问题的主旨是关于如何将comment_text 变量放入SplitComment 函数。目前它根本不知道comment_text 存在,所以我不能使用它。

标签: sql-server sql-server-2012 common-table-expression


【解决方案1】:

您可能正在寻找类似的东西(它是一个简化版本,我只使用名字和comment_date 作为“标识符”)。 我使用这些数据对其进行了测试,并且 - 目前 - 将 max len 50 成像以拆分文本列。 提示:将 comment_text 数据类型更改为 VARCHAR(500)

DECLARE @Comments TABLE
(
    id INT NOT NULL IDENTITY PRIMARY KEY CLUSTERED,
    comment_date DATETIME NOT NULL,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    comment_title VARCHAR(50) NOT NULL,
    comment_text VARCHAR(500)
);

    INSERT INTO @Comments VALUES(CURRENT_TIMESTAMP, 'Bob', 'Example','Bob''s Comment', 'Text of Bob''s comment.');
    INSERT INTO @Comments VALUES(CURRENT_TIMESTAMP, 'Alice', 'Example','Alice''s Comment'
    , 'Text of Alice''s comment that is much longer and will need to be split over multiple rows aaaaaa bbbbbb cccccc ddddddddddd eeeeeeeeeeee fffffffffffff ggggggggggggg.');

WITH CTE AS (SELECT comment_date, first_name, '<Note>'+CAST( SUBSTRING(comment_text, 1, 50) AS VARCHAR(500)) +'</Note>'comment_text, 1 AS RN
             FROM @Comments 
             UNION ALL 
             SELECT A.comment_date, A.first_name, '<Text>'+CAST( SUBSTRING(A.comment_text, B.RN*50+1, 50) AS VARCHAR(500)) +'</Text>'AS comment_text, B.RN+1 AS RN
             FROM @Comments A 
             INNER JOIN CTE B ON A.comment_date=B.comment_date AND A.first_name=B.first_name 
            WHERE  LEN(A.comment_text) > B.RN*50+1                    
             )
SELECT A.comment_date, A.first_name, '<title>'+ comment_title+'</title>' AS markup  
FROM @Comments A
UNION ALL
SELECT B.comment_date, B.first_name, B.comment_text AS markup  
FROM  CTE B ;

输出:

    comment_date        first_name  markup
2017-07-07 14:30:51.117 Bob         <title>Bob's Comment</title>
2017-07-07 14:30:51.117 Alice       <title>Alice's Comment</title>
2017-07-07 14:30:51.117 Bob          <Note>Text of Bob's comment.</Note>
2017-07-07 14:30:51.117 Alice        <Note>Text of Alice's comment that is much longer and wi</Note>
2017-07-07 14:30:51.117 Alice        <Text>ll need to be split over multiple rows aaaaaa bbbb</Text>
2017-07-07 14:30:51.117 Alice        <Text>bb cccccc ddddddddddd eeeeeeeeeeee fffffffffffff g</Text>
2017-07-07 14:30:51.117 Alice        <Text>gggggggggggg.</Text>

【讨论】:

  • 走这条路当然是一个可行的选择,但在我面前的实际编程任务中,这样做会带来很多变化和开销。我问这个问题是为了找到一种对一组现有代码产生最小影响的方法。不过谢谢!
  • 您在寻找哪种解决方案?在我看来,您正在寻找 CTE 查询
  • 抱歉,我今天做这件事太久了,当我试图将您的答案移植回我的原始代码时,我感到很困惑。你的答案是正确的,我只是愚蠢。谢谢!
  • 如果你想改变 Bob ,在 cte 的第一行使用 CASE 很容易做到(我不确定你想要这个,或者它是否是你的任务中的类型错误.)
  • 记得把 50 换成 500(我用 50 来简化我的测试)。
【解决方案2】:

这是一个还允许对结果集进行排序的解决方案

它使用递归 CTE 来计算长文本中的位置。 通过将表格连接到 CTE,可以将文本分割成行。

with cte as 
(
  select id, 1 as lvl, len(comment_text) as posmax, 1 pos1, 50 limit
  from @Comments
  union all
  select id, lvl + 1, posmax, iif(pos1+limit<posmax,pos1+limit,posmax), limit
  from cte
  where pos1+limit<posmax
)
, CTE2 AS 
(
select id, 0 as lvl,
 comment_date,
 concat(first_name,' ',last_name) as commenter,
 '<Title>'+rtrim(comment_title)+'</Title>' as markup
from @Comments
union all
select t.id, c.lvl,
 comment_date,
 concat(first_name,' ',last_name) as commenter_name,
 concat(iif(lvl=1,'<Note>','<Text>'),substring(comment_text,pos1,limit),iif(lvl=1,'</Note>','</Text>')) as markup
from @Comments t 
join cte c on c.id = t.id
)
select comment_date, commenter, markup 
from CTE2
order by id, lvl;

输出:

comment_date             commenter     markup
-----------------------  ------------  -------------------------------------
2017-07-07 15:06:31.293  Bob Example   <Title>Bob's Comment</Title>
2017-07-07 15:06:31.293  Bob Example   <Note>Text of Bob's comment.</Note>
2017-07-07 15:06:31.293  Alice Example <Title>Alice's Comment</Title>
2017-07-07 15:06:31.293  Alice Example <Note>Text of Alice's comment that is much longer and wi</Note>
2017-07-07 15:06:31.293  Alice Example <Text>ll need to be split over multiple rows.</Text>

【讨论】:

    猜你喜欢
    • 2021-11-10
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    相关资源
    最近更新 更多