CTE error: "Types don't match between the anchor and the recursive part" - sql

I am executing the following statement:
;WITH cte AS (
SELECT
1 as rn,
'name1' as nm
UNION ALL
SELECT
rn + 1,
nm = 'name' + CAST((rn + 1) as varchar(255))
FROM cte a WHERE rn < 10)
SELECT *
FROM cte
...which finishes with the error...
Msg 240, Level 16, State 1, Line 2
Types don't match between the anchor and the recursive part in column "nm" of recursive query "cte".
Where am I making the mistake?

Exactly what it says:
'name1' has a different data type to 'name' + CAST((rn+1) as varchar(255))
Try this (untested)
;with cte as
(
select 1 as rn, CAST('name1' as varchar(259)) as nm
union all
select rn+1,nm = 'name' + CAST((rn+1) as varchar(255))
from cte a where rn<10)
select * from cte
Basically, you have to ensure the length matches too. For the recursive bit, you may have to use CAST('name' AS varchar(4)) if it fails again

You need to cast both nm fields
;with cte as
(
select 1 as rn,
CAST('name1' AS VARCHAR(255)) as nm
union all
select rn+1,
nm = CAST('name' + CAST((rn+1) as varchar(255)) AS VARCHAR(255))
from cte a where rn<10)
select * from cte

For me problem was in different collation.
Only this helped me:
;WITH cte AS (
SELECT
1 AS rn,
CAST('name1' AS NVARCHAR(4000)) COLLATE DATABASE_DEFAULT AS nm
UNION ALL
SELECT
rn + 1,
nm = CAST('name' + CAST((rn + 1) AS NVARCHAR(255)) AS NVARCHAR(4000)) COLLATE DATABASE_DEFAULT
FROM cte a WHERE rn < 10)
SELECT *
FROM cte;
Hope it can help someone else.

;with cte as
(
select 1 as rn, 'name' + CAST(1 as varchar(255)) as nm
union all
select rn+1,nm = 'name' + CAST((rn+1) as varchar(255))
from cte a where rn<10)
select * from cte

In my case, I messed up the sequence of columns in top and bottom clauses of UNION ALL. And it turned out that a varchar column appeared 'under' an int one. An easy mistake to make of you have lots of columns

If you use CONCAT in the recursive term of a rcte, since the output type of concat is varchar(MAX), you only need to cast the column in the initial query:
WITH rcte AS (
SELECT 1 AS nr, CAST('1' AS varchar(MAX)) AS trail
UNION ALL
SELECT nr+1, CONCAT(trail, '/', nr+1)
FROM rcte
WHERE nr < 5
)
SELECT * FROM rcte;

I would recommend using nvarchar(max)
WITH CTE AS (
SELECT x,x_name FROM (VALUES (1,CAST('' AS nvarchar(MAX)))) AS test(x,x_name)
UNION ALL
SELECT x + 1 x, CONCAT(x_name,x+1) FROM CTE WHERE x < 10 )
SELECT * FROM CTE

WITH rcte AS (
SELECT 1 AS nr, CAST('1' AS varchar(MAX)) AS trail
UNION ALL
SELECT nr+1, cast(CONCAT(trail, '/', nr+1) as varchar(max))
FROM rcte
WHERE nr < 5
)
SELECT * FROM rcte;

;with tmp1(NewsId,DataItem ,HeaderText)
as
(
select NewsId, LEFT(HeaderText, CHARINDEX(',',HeaderText+',')-1),
STUFF(HeaderText, 1, CHARINDEX(',',HeaderText+','), '')
from Currentnews
union all
select NewsId, LEFT(HeaderText, CHARINDEX(',',HeaderText+',')-1),
STUFF(HeaderText, 1, CHARINDEX(',',HeaderText+','), '')
from tmp1
where HeaderText > ''
)
select NewsId, DataItem
from tmp1
order by NewsId

Related

SQL split and merge string

this is my problem:
I got a string from a column like this:
**0756FJ89045GJD38**.pdf
Now i have to generate a path by this string:
/home/ars/07/56/FJ/89/04/5G/JD/38/0756FJ89045GJD38.pdf
I have to take two characters and build it up to one path level from left to right.
Maybe u can help me, thanks!
This may help:
DECLARE #p nvarchar(100) = '**0756FJ89045GJD38**.pdf',
#n int = 3
;WITH cte AS (
SELECT STUFF(REPLACE(SUBSTRING(#p,1,CHARINDEX('.',#p)-1),'*',''),1,0,'/') as p, 1 [level]
UNION ALL
SELECT STUFF(p,[level]+#n,0,'/'), [level]+#n
FROM CTE
WHERE LEN(STUFF(p,[level]+#n,0,'/')) >= [level]+#n
)
SELECT TOP 1 #p = '/home/ars'+p +'/'+REPLACE(#p,'*','')
FROM cte
ORDER BY [level] DESC
SELECT #p
Output:
/home/ars/07/56/FJ/89/04/5G/JD/38/0756FJ89045GJD38.pdf
EDIT:
If there is a table with PDF file names and all names are equal size, than you can do this way:
DECLARE #n int = 3
;WITH pdf AS (
SELECT *
FROM (VALUES
('**0756FJ89045GJD38**.pdf'),
('**1729DA8CD189700A**.pdf'),
('**A6710936BCD47832**.pdf'),
('**00A764D617B93978**.pdf')
) as t(file_)
)
,cte AS (
SELECT file_, STUFF(REPLACE(SUBSTRING(file_,1,CHARINDEX('.',file_)-1),'*',''),1,0,'/') as p, 1 [level]
FROM pdf
UNION ALL
SELECT file_, STUFF(p,[level]+#n,0,'/'), [level]+#n
FROM CTE
WHERE LEN(STUFF(p,[level]+#n,0,'/')) >= [level]+#n
)
SELECT TOP 1 WITH TIES '/home/ars'+p +'/' + REPLACE(c.file_,'**','')
FROM cte c
ORDER BY ROW_NUMBER() OVER (PARTITION BY file_ ORDER BY [level]) DESC
Output:
/home/ars/00/A7/64/D6/17/B9/39/78/00A764D617B93978.pdf
/home/ars/A6/71/09/36/BC/D4/78/32/A6710936BCD47832.pdf
/home/ars/17/29/DA/8C/D1/89/70/0A/1729DA8CD189700A.pdf
/home/ars/07/56/FJ/89/04/5G/JD/38/0756FJ89045GJD38.pdf

SQL splitting a word in separate characters

I need to change an application and the first thing I need is to change a field in a database table.
In this table I now have 1 to 6 single characters, i.e. 'abcdef'
I need to change this to '[a][b][c][d][e][f]'
[edit] It is meant to stay in the same field. So before field = 'abcdef' and after field = '[a][b][c][d][e][f]'.
What would be a good way to do this?
rg.
Eric
You can split string to separate characters using following function:
create function ftStringCharacters
(
#str varchar(100)
)
returns table as
return
with v1(N) as (
select 1 union all select 1 union all select 1 union all select 1 union all select 1
union all
select 1 union all select 1 union all select 1 union all select 1 union all select 1
),
v2(N) as (select 1 from v1 a, v1 b),
v3(N) as (select top (isnull(datalength(#str), 0)) row_number() over (order by ##spid) from v2)
select N, substring(#str, N, 1) as C
from v3
GO
And then apply it as:
update t
set t.FieldName = p.FieldModified
from TableName t
cross apply (
select (select quotename(s.C)
from ftStringCharacters(t.FieldName) s
order by s.N
for xml path(''), type).value('text()[1]', 'varchar(20)')
) p(FieldModified)
SQLFiddle sample
DECLARE #text NVARCHAR(50)
SET #text = 'abcdef'
DECLARE #texttable TABLE (value NVARCHAR(1))
WHILE (len(#text) > 0)
BEGIN
INSERT INTO #texttable
SELECT substring(#text, 1, 1)
SET #text = stuff(#text, 1, 1, '')
END
select * from #texttable
Without using a function:
declare #t table(C varchar(18))
insert #t values('abc'), ('1234'), (' 1234a')
;with CTE as
(
select C, '[' + substring(c, a.n, 1) + ']' v, rn from
(select 1 n union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6) a
cross apply
(select c, row_number() over (order by C) rn from #t group by c) b
where a.n <= len(C)
)
update t3
set C = t4.[value]
FROM #t t3
JOIN
(
select C,
(
select v
from CTE t1
where t1.rn = t2.rn
for xml path(''), type
).value('.', 'varchar(18)') [value]
from CTE t2
group by t2.rn, C
) t4
ON t3.C = t4.C
SELECT * FROM #t

Return Distinct Rows That Contain The Same Value/Character In SQL

I have a bit of a tricky situation. I have a column that contains a pipe delimited set of numbers in numerous rows in a table. For example:
Courses
-------------------
1|2
1|2|3
1|2|8
10
11
11|12
What I want to achieve is to return rows where the number only appears once in my output.
Ideally, I want to try and carry this out using SQL rather than having to carry out checks at a web application level. Carrying out a DISTINCT does not achieve what I want.
The desired output would be:
Courses
-------------------
1
2
3
8
10
11
12
I would appreciated if anyone can guide me in the right direction.
Thanks.
Please try:
declare #tbl as table(Courses nvarchar(max))
insert into #tbl values
('1|2'),
('1|2|3'),
('1|2|8'),
('10'),
('11'),
('11|12')
select * from #tbl
SELECT
DISTINCT CAST(Split.a.value('.', 'VARCHAR(100)') AS INT) AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE(Courses, '|', '</M><M>') + '</M>' AS XML) AS CVS
FROM #tbl
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)
ORDER BY 1
Try this one -
SET NOCOUNT ON;
DECLARE #temp TABLE
(
string VARCHAR(500)
)
DECLARE #Separator CHAR(1)
SELECT #Separator = '|'
INSERT INTO #temp (string)
VALUES
('1|2'),
('1|2|3'),
('1|2|8'),
('10'),
('11'),
('11|12')
-- 1. XML
SELECT p.value('(./s)[1]', 'VARCHAR(500)')
FROM (
SELECT field = CAST('<r><s>' + REPLACE(t.string, #Separator, '</s></r><r><s>') + '</s></r>' AS XML)
FROM #temp t
) d
CROSS APPLY field.nodes('/r') t(p)
-- 2. CTE
;WITH a AS
(
SELECT
start_pos = 1
, end_pos = CHARINDEX(#Separator, t.string)
, t.string
FROM #temp t
UNION ALL
SELECT
end_pos + 1
, CHARINDEX(#Separator, string, end_pos + 1)
, string
FROM a
WHERE end_pos > 0
)
SELECT d.name
FROM (
SELECT
name = SUBSTRING(
string
, start_pos
, ABS(end_pos - start_pos)
)
FROM a
) d
WHERE d.name != ''
Try this :
create table course (courses varchar(100))
insert into course values('1|2')
insert into course values('1|2|3')
insert into course values('1|2|8')
insert into course values('10')
insert into course values('11')
insert into course values('11|12')
Declare #col varchar(200)
SELECT
#col=(
SELECT DISTINCT c.courses + '|'
FROM course c
FOR XML PATH('')
);
select * from course
;with demo as(
select cast(substring(#col,1,charindex('|',#col,1)-1) AS INT) cou,charindex('|',#col,1) pos
union all
select cast(substring(#col,pos+1,charindex('|',#col,pos+1)-pos-1)AS INT) cou,charindex('|',#col,pos+1) pos
from demo where pos<LEN(#col))
select distinct cou from demo
Could not manage without recursion :( Something like this could do the trich?
WITH splitNum(num, r)
AS
(
SELECT
SUBSTRING(<field>,1, CHARINDEX('|', <field>)-1) num,
SUBSTRING(<field>,CHARINDEX('|', <field>)+1, len(<field>)) r
FROM <yourtable> as a
UNION ALL
SELECT
SUBSTRING(r,1, CHARINDEX('|', r)-1) num,
SUBSTRING(r,CHARINDEX('|', r)+1, len(r)) r
FROM <yourtable> b
WHERE CHARINDEX('|', r) > 0
inner join splitNum as c on <whatevertheprimarykeyis>
)
SELECT distinct num FROM splitNum
Didn't make it run, but it should do the trick, just replace the and with the correct info
One way would be to use a recursive CTE:
with cte as
(select cast(case charindex('|',courses) when 0 then courses
else left(courses,charindex('|',courses)-1) end as int) course,
case charindex('|',courses) when 0 then ''
else right(courses,len(courses)-charindex('|',courses)) end courses
from courses
union all
select cast(case charindex('|',courses) when 0 then courses
else left(courses,charindex('|',courses)-1) end as int) course,
case charindex('|',courses) when 0 then ''
else right(courses,len(courses)-charindex('|',courses)) end courses
from cte
where len(courses)>0)
select distinct course from cte
SQLFiddle here.

SQL query problem

Let´s say I have two tables, "Garden" and "Flowers". There is a 1:n-relationship between these tables, because in a garden can be many flowers. Is it possible to write an SQL query which returns a result with the following structure:
GardenName Flower1Name Flower2Name .... (as long as there are entries in flowers)
myGarden rose tulip
CREATE TABLE #Garden (Id INT, Name VARCHAR(20))
INSERT INTO #Garden
SELECT 1, 'myGarden' UNION ALL
SELECT 2, 'yourGarden'
CREATE TABLE #Flowers (GardenId INT, Flower VARCHAR(20))
INSERT INTO #Flowers
SELECT 1, 'rose' UNION ALL
SELECT 1, 'tulip' UNION ALL
SELECT 2, 'thistle'
DECLARE #ColList nvarchar(max)
SELECT #ColList = ISNULL(#ColList + ',','') + QUOTENAME('Flower' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS VARCHAR))
FROM #Flowers WHERE GardenId = (
SELECT TOP 1 GardenId
FROM #Flowers
ORDER BY COUNT(*) OVER (PARTITION BY GardenId) DESC
)
EXEC (N'
;WITH cte As
(
SELECT *, ''Flower'' + CAST(ROW_NUMBER() OVER (PARTITION BY GardenId ORDER BY (SELECT 0)) AS VARCHAR) RN
FROM #Flowers F
)
SELECT Name,' + #ColList + N'
FROM cte
JOIN #Garden g ON g.Id = GardenId
PIVOT (MAX(Flower) FOR RN IN (' + #ColList + N')) Pvt')
DROP TABLE #Garden
DROP TABLE #Flowers
Returns
Name Flower1 Flower2
-------------------- -------------------- --------------------
myGarden rose tulip
yourGarden thistle NULL
Look at using Pivot in SQL Server. Here is a good link that goes over how it works:
http://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
Ok, i think i got it working. Try this:
with temp as
(
select 'myGarden' as name, 'test1' as flower
union
select 'myGarden','test2'
union
select 'myGarden','test5'
union
select 'abeGarden','test4'
union
select 'abeGarden','test5'
union
select 'martinGarden', 'test2'
)
select* from temp
pivot
(
max(flower)
for flower in (test1,test2,test3,test4,test5)
) PivotTable
You could also make the values in the in clause dynamic. Since this is a CTE i can't in my example.
Dynamic SQL with a cursor is the only way I can think of, and it won't be pretty.
If you only want the results for one garden at a time this would give you the data:
select gardenName from tblGarden where gardenid = 1
Union ALL
select tblFLowers.flowerName from tblFlowers where gardenid = 1

What is wrong in the below cte

I have
;with cte as
(
select rn=1, name = CAST('name'as varchar(50))
union all
select rn+1, CAST(name as varchar(50))+ CAST( (rn+1) as varchar(50))
from cte where rn<100)
select * from cte
error
Msg 240, Level 16, State 1, Line 1
Types don't match between the anchor and the recursive part in column "name" of recursive query "cte".
Try something like
;with cte as
(
select rn=1,
name = CAST('name'as varchar(100))
union all
select rn+1,
CAST(name as varchar(50))+ CAST( (rn+1) as varchar(50))
from cte where rn<100)
select * from cte
You have to remember that all fields in the anchor and the recursive part have to be of the same type. That also goes for varchar fields.