问题
I have some data that I need to output as rows containing markup tags, which I'm doing inside a table valued function.
This has been working fine up to a point using code in the format below, using the search
query to gather my data, and then inserting into my returned table using the output from results
.
I now need to take a longer data field and split it up over a number of rows, and I'm at something of a loss as to how to achieve this.
I started with the idea that I wanted to use a CTE to process the data from my query, but I can't see a way to get the data from my search
query into my CTE and from there into my results
set.
I guess I can see an alternative way of doing this by creating another table valued function in the database that returns a results set if I feed it my comment_text
column, but it seems like a waste to do it that way.
Does anyone see a route through to a solution?
Example "Real" Table:
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)
);
Add Comment Rows:
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.');
Format of returned results table:
DECLARE @return_table TABLE
(
comment_date DATETIME,
commenter_name VARCHAR(101),
markup VARCHAR(100)
);
Naive query (Can't run because the variable comment_text
in the SplitComment CTE can't be identified.
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;
Results (when the function is run without the 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>
Ideally, I'd like to get one additional row for Bob's comment, and two rows for Alice's comment. Something like this:
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>
回答1:
May be you are looking for something like this (it' a simplified version, I used only first name and comment_date as "identifier"). I tested it using this data and - for the moment - imaging max len 50 to split text column. Tip: change comment_text datatype to 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 ;
Output:
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>
回答2:
Here's a solution that also allows sorting the resultset
It uses a recursive CTE to calculate the positions in the long text. And by joining the table to the CTE, the text can be sliced up into rows.
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;
Output:
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>
来源:https://stackoverflow.com/questions/44968576/using-a-cte-to-split-results-across-a-cross-apply