I have following data
I want to Convert Column to Row
I have use following Query ::
Select Distinct [ApplicantID],
Split.a.value('.', 'VARCHAR(100)') AS Subjects,
hoursperweek,[Days],[Time]
From ( Select
Cast ('<M>' + REPLACE([Subjects], ',', '</M><M>') + '</M>' AS XML)
AS Subjects , ApplicantID, hoursperweek,[Days],[Time]
From [dbo].[Progrm])
AS A CROSS APPLY Subjects.nodes ('/M') AS Split(a)
It give me following Result
but I want to seprate Subjects, Hoursperweek, days, time. How can I do that ?
ApplicantID Subjects HoursPerWeek Days Time
1 a 12 Sun 12:45:12
1 b 25 Mon 14:45:12
Here is a function to split the Comma separated values into Rows
CREATE FUNCTION dbo.Splitstrings (#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255))
RETURNS #split_tab TABLE (
RN INT IDENTITY(1, 1),
SPLIT_VAL VARCHAR(100))
WITH SCHEMABINDING
AS
BEGIN ;
;WITH E1(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),
E2(N) AS (SELECT 1 FROM E1 a,E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N)
AS (SELECT 0
UNION ALL
SELECT TOP (Datalength(Isnull(#List, 1))) Row_number()OVER (
ORDER BY (SELECT NULL))
FROM E42),
cteStart(N1)
AS (SELECT t.N + 1
FROM cteTally t
WHERE ( Substring(#List, t.N, 1) = #Delimiter
OR t.N = 0 ))
INSERT INTO #split_tab
SELECT SPLIT_VAL= Substring(#LIST, S.N1, Isnull(NULLIF(Charindex(#DELIMITER, #LIST, S.N1), 0) - S.N1, 8000))
FROM CTESTART S;
RETURN
END
Now the ugly code to split the data
;WITH data
AS (SELECT 1 ApplicantID,
'a,b' Subjects,
'12,25' HoursPerWeek,
'sun,mon' Days,
'12:45:12,14:45:12' Time),
sub
AS (SELECT RN,
ApplicantID,
Subjects=SPLIT_VAL
FROM data
CROSS apply Splitstrings (Subjects, ',')),
HPW
AS (SELECT RN,
ApplicantID,
HoursPerWeek=SPLIT_VAL
FROM data
CROSS apply Splitstrings (HoursPerWeek, ',')),
Days
AS (SELECT RN,
ApplicantID,
Days=SPLIT_VAL
FROM data
CROSS apply Splitstrings (Days, ',')),
Time
AS (SELECT RN,
ApplicantID,
Time=SPLIT_VAL
FROM data
CROSS apply Splitstrings (Time, ','))
SELECT D.APPLICANTID,
S.SUBJECTS,
H.HOURSPERWEEK,
DA.DAYS,
T.TIME
FROM DATA D
FULL OUTER JOIN SUB S
ON D.APPLICANTID = S.APPLICANTID
FULL OUTER JOIN HPW H
ON D.APPLICANTID = H.APPLICANTID
AND H.RN = S.RN
FULL OUTER JOIN DAYS DA
ON D.APPLICANTID = DA.APPLICANTID
AND DA.RN = COALESCE(S.RN, H.RN)
FULL OUTER JOIN TIME T
ON D.APPLICANTID = T.APPLICANTID
AND T.RN = COALESCE(S.RN, H.RN, DA.RN)
Result:
╔═════════════╦══════════╦══════════════╦══════╦══════════╗
║ APPLICANTID ║ SUBJECTS ║ HOURSPERWEEK ║ DAYS ║ TIME ║
╠═════════════╬══════════╬══════════════╬══════╬══════════╣
║ 1 ║ a ║ 12 ║ sun ║ 12:45:12 ║
║ 1 ║ b ║ 25 ║ mon ║ 14:45:12 ║
╚═════════════╩══════════╩══════════════╩══════╩══════════╝
This is the reason we should never store comma separated values into single column. When you want to parse the data things get difficult and we need to such ugly codes to parse the data
Related
I have a table with a column Shape of geometry datatype.
This is the data in Shape :
POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.9837 2127263.49974102))
POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.2816807 2127097.94105044))
and so on....
I need output as
ID | X | Y
---+-----------------+-----------------
1 | 565542.98375 | 2127263.4997410
1 | 565538.48450 | 2127261.3187302
1 | 565541.96658 | 2127254.1162
1 | 565546.465835 | 2127256.297297
1 | 565542.9837 | 2127263.49974102
2 | 565547.281627 | 2127097.9410014
2 | 565549.457915 | 2127093.43948425
2 | 565553.5774391 | 2127084.9189882
and so on in table format
If you first create a Numbers table with sequential integers from 1 upwards and at least as many rows as the maximum number of points you will ever be dealing with then this is straightforward.
SELECT S.id,
X = S.GeomCol1.STPointN(N.number).STX,
Y = S.GeomCol1.STPointN(N.number).STY
FROM SpatialTable S
JOIN Numbers N
ON N.number <= S.GeomCol1.STNumPoints()
Code to create and populate a Numbers table is below.
CREATE TABLE dbo.Numbers(Number INT PRIMARY KEY);
WITH E1(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
) -- 1*10^1 or 10 rows
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) -- 1*10^2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) -- 1*10^4 or 10,000 rows
, E8(N) AS (SELECT 1 FROM E4 a, E4 b) -- 1*10^8 or 100,000,000 rows
, Nums AS (SELECT TOP (10000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS N FROM E8)
INSERT INTO dbo.Numbers
SELECT N
FROM Nums
Here is an option which will work with 2012. Note that we maintain the sequence (RetSeq)
Example
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
Returns
ID RetSeq X Y
1 1 565542.98375 2127263.499741
1 2 565538.4845 2127261.3187302
1 3 565541.96658 2127254.1162
1 4 565546.465835 2127256.297297
1 5 565542.98375 2127263.499741
2 1 565547.281621307 2127097.9410014
2 2 565549.457915 2127093.43948425
2 3 565553.577449391 2127084.9189882
2 4 565568.882475 2127092.31709055
2 5 565562.586805441 2127105.3404182
2 6 565547.281621307 2127097.9410014
EDIT
Martin Smith's solution should really be the ACCEPTED answer. If you can't create a numbers table, you can use an ad-hoc tally table.
Example
Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints()) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
Requested EDIT
;with cte as (
SELECT A.ID
,B.RetSeq
,X = left(C.RetVal,charindex(' ',C.RetVal)-1)
,Y = substring(C.RetVal,charindex(' ',C.RetVal)+1,50)
,Cnt = max(B.RetSeq) over (Partition by A.ID)
FROM YourTable A
Cross Apply (
Select RetSeq = row_number() over (Order By 1/0)
,RetVal = B2.i.value('(./text())[1]', 'varchar(100)')
From (Select x = Cast('<x>' + replace(A.Shape.STAsText(),',','</x><x>')+'</x>' as xml)) as B1
Cross Apply x.nodes('x') AS B2(i)
) B
Cross Apply ( values (ltrim(rtrim(replace(replace(replace(B.RetVal,'POLYGON',''),'(',''),')','')))) ) C(RetVal)
)
Select *
From cte
Where RetSeq<Cnt
Order By ID,RetSeq
OR ... Notice the minus 1 in the TOP
Select A.ID
,Seq = B.N
,X = Shape.STPointN(N).STX
,Y = Shape.STPointN(N).STY
From YourTable A
Cross Apply (Select Top (Shape.STNumPoints() - 1) N=Row_Number() Over (Order By 1/0) From master..spt_values n1, master..spt_values n2 ) B
Naive approach with parsing string(SQL Server 2017):
CREATE TABLE SpatialTable
( id int IDENTITY (1,1),
GeomCol1 geometry );
INSERT INTO SpatialTable
SELECT geometry::STGeomFromText(
'POLYGON ((565542.98375 2127263.4997410, 565538.48450 2127261.3187302, 565541.96658 2127254.1162, 565546.465835 2127256.297297, 565542.98375 2127263.4997410))',0)
UNION ALL
SELECT geometry::STGeomFromText('POLYGON ((565547.281621307 2127097.9410014, 565549.457915 2127093.43948425, 565553.577449391 2127084.9189882, 565568.882475 2127092.31709055, 565562.586805441 2127105.3404182, 565547.281621307 2127097.9410014))',0);
Query:
SELECT ID, s1.rn, s3.x, s3.y, GeomCol1
FROM SpatialTable s
CROSS APPLY (SELECT value, ROW_NUMBER() OVER(ORDER BY 1/0) AS rn
FROM STRING_SPLIT(s.GeomCol1.STAsText() ,',')) s1
CROSS APPLY (SELECT TRIM(TRANSLATE(value, 'POLYGON()', ' '))) s2(r)
CROSS APPLY (SELECT TRY_CAST(LEFT(s2.r, CHARINDEX(' ',s2.r)) AS DECIMAL(18,6)),
TRY_CAST(RIGHT(s2.r,LEN(s2.r)-CHARINDEX(' ',s2.r)) AS DECIMAL(18,6))
) s3(x,y);
db<>fiddle demo
You can use the geometry methods in a CROSS APPLY to get those values.
Then get the X and Y from the points.
In the example below the numbers come from spt_values, but that's just 1 one of the methods to obtain a Tally table with numbers in a range.
WITH NUMS AS
(
SELECT DISTINCT number as n
FROM master..[spt_values]
WHERE number between 1 and 128
)
SELECT ID, GeoPoint.STX AS X, GeoPoint.STY AS Y
FROM Shapes s
CROSS APPLY
(
SELECT n as PointN, Shape.STPointN(n) AS GeoPoint
FROM NUMS
WHERE n BETWEEN 1 AND Shape.STNumPoints()
) ca;
Test on db<>fiddle here
I have 3 columns in a table, in which 2 columns have string separated by a '|' pipe. Both these columns values depend on each other.
For an example: I have data in the table like this :
ID product quantity
1 A|B|C 1|2|3
2 X|Y|Z 7|8|9
I would like to change it to something like this :
ID product quantity
1 A 1
1 B 2
1 C 3
2 X 7
2 Y 8
2 Z 9
As i am working with SSMS, i don't have any other choice except SQL. I try to use cross apply but i am not getting right result. For 1 row i receive 9 rows instead of getting 3.
could anyone suggest me which method should i use?
Thank you in advance!!
JACK
This is rather tricky, because you need for the values to match up. The following takes a recursive CTE approach:
with cte as (
select id,
left(product, charindex('|', product + '|') - 1) as product,
left(quantity, charindex('|', quantity + '|') - 1) as quantity,
substring(product, charindex('|', product + '|') + 1, 1000) as products,
substring(quantity, charindex('|', quantity + '|') + 1, 1000) as quantities
from t
union all
select id,
left(products, charindex('|', products + '|') - 1) as product,
left(quantities, charindex('|', quantities + '|') - 1) as quantity,
substring(products, charindex('|', products + '|') + 1, 1000) as products,
substring(quantities, charindex('|', quantities + '|') + 1, 1000) as quantities
from cte
where products <> '' and quantities <> ''
)
select id, product, quantity
from cte;
Here is a little Rextester.
Test Data
CREATE TABLE #t (ID INT, product VARCHAR(100) , quantity VARCHAR(100) )
INSERT INTO #t VALUES
(1 ,'A|B|C' , '1|2|3'),
(2 ,'X|Y|Z' , '7|8|9');
Query
WITH Products AS (
SELECT ID
, Product_Split.a.value('.', 'VARCHAR(100)') Products
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL)) rn
FROM (
SELECT ID
,Cast ('<X>'
+ Replace(product, '|', '</X><X>')
+ '</X>' AS XML) AS Product_Data
FROM #t
) AS t
CROSS APPLY Product_Data.nodes ('/X') AS Product_Split(a)
),
Quantities AS (
SELECT ID
, Quantity_Split.a.value('.', 'VARCHAR(100)') Quantity
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY (SELECT NULL)) rn
FROM (
SELECT ID
,Cast ('<X>'
+ Replace(quantity, '|', '</X><X>')
+ '</X>' AS XML) AS Quantity_Data
FROM #t
) AS t
CROSS APPLY Quantity_Data.nodes ('/X') AS Quantity_Split(a)
)
SELECT t.ID
, P.Products
, Q.Quantity
FROM #t t
LEFT JOIN Products P ON t.ID = p.ID
LEFT JOIN Quantities Q ON Q.ID = t.ID
AND Q.rn = p.rn
Result Set
╔════╦══════════╦══════════╗
║ ID ║ Products ║ Quantity ║
╠════╬══════════╬══════════╣
║ 1 ║ A ║ 1 ║
║ 1 ║ B ║ 2 ║
║ 1 ║ C ║ 3 ║
║ 2 ║ X ║ 7 ║
║ 2 ║ Y ║ 8 ║
║ 2 ║ Z ║ 9 ║
╚════╩══════════╩══════════╝
splitting strings is easy, there are tons of examples. The tricky part here is to connect the fragments via their position. My suggestion uses XMLs abilities to target an element by its position:
DECLARE #tbl TABLE(ID INT, product VARCHAR(100) , quantity VARCHAR(100) )
INSERT INTO #tbl VALUES
(1 ,'A|B|C' , '1|2|3')
,(2 ,'X|Y|Z' , '7|8|9');
--This is the query
WITH CastedToXML AS
(
SELECT *
,CAST('<x>' + REPLACE(product,'|','</x><x>') + '</x>' AS XML) AS ProductXml
,CAST('<x>' + REPLACE(quantity,'|','</x><x>') + '</x>' AS XML) AS QuantityXml
FROM #tbl
)
SELECT *
,ProductXml.value('/x[sql:column("Nmbr")][1]','nvarchar(10)') AS ProductAtPosition
,QuantityXml.value('/x[sql:column("Nmbr")][1]','int') AS QuantityAtPosition
FROM CastedToXML
--Create a set of running numbers (spt_values is just a pre-filled table with many rows)
CROSS APPLY (SELECT TOP(CastedToXML.ProductXml.value('count(/x)','int'))
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM master..spt_values) AS Tally(Nmbr);
the result
+----+------+-------------------+--------------------+
| ID | Nmbr | ProductAtPosition | QuantityAtPosition |
+----+------+-------------------+--------------------+
| 1 | 1 | A | 1 |
+----+------+-------------------+--------------------+
| 1 | 2 | B | 2 |
+----+------+-------------------+--------------------+
| 1 | 3 | C | 3 |
+----+------+-------------------+--------------------+
| 2 | 1 | X | 7 |
+----+------+-------------------+--------------------+
| 2 | 2 | Y | 8 |
+----+------+-------------------+--------------------+
| 2 | 3 | Z | 9 |
+----+------+-------------------+--------------------+
Some explanation:
the cast to xml transfers your A|B|C to
<x>A</x>
<x>B</x>
<x>C</x>
This list is joined with a number set created on the fly using the count of <x> as the TOP limit.
Now it is easy to pick the <x> out of your XML by the position.
Try it out!
UPDATE: Non-unique IDs
DECLARE #tbl TABLE(ID INT, product VARCHAR(100) , quantity VARCHAR(100) )
INSERT INTO #tbl VALUES
(1 ,'A|B|C' , '1|2|3')
,(2 ,'X|Y|Z' , '7|8|9')
,(3 ,'a|b|c' , '7|8|9')
,(2 ,'D|e|f' , '7|8|9')
;
--This is the query
WITH CastedToXML AS
(
SELECT *
,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ID) AS RowIndex
,CAST('<x>' + REPLACE(product,'|','</x><x>') + '</x>' AS XML) AS ProductXml
,CAST('<x>' + REPLACE(quantity,'|','</x><x>') + '</x>' AS XML) AS QuantityXml
FROM #tbl
)
SELECT *
,ProductXml.value('/x[sql:column("Nmbr")][1]','nvarchar(10)') AS ProductAtPosition
,QuantityXml.value('/x[sql:column("Nmbr")][1]','int') AS QuantityAtPosition
FROM CastedToXML
--Create a set of running numbers (spt_values is just a pre-filled table with many rows)
CROSS APPLY (SELECT TOP(CastedToXML.ProductXml.value('count(/x)','int'))
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM master..spt_values) AS Tally(Nmbr);
SQL Server: I would like to create a function that removes specific characters from a string, based on parameters.
parameter1 is original string
parameter2 is characters want to removed from original string
For example :
call MyRemoveFunc('32.87.65.54.89', '87.65' ) -- this will return '32.54.89'
call MyRemoveFunc('11.23.45', '23' ) -- this will return '11.45'
call MyRemoveFunc('14.99.16.84', '84.14' ) -- this will return '99.16'
call MyRemoveFunc('11.23.45.65.31.90', '23' ) -- this will return 11.45.65.31.90
call MyRemoveFunc('34.35.36', '35' ) -- this will return 34.36
call MyRemoveFunc('34.35.36.76.44.22', '35' ) -- this will return 34.36.76.44.22
call MyRemoveFunc('34', '34' ) -- this will return blank
call MyRemoveFunc('45.23.11', '45.11' ) -- this will return 23
Thanks
Here is one way using Recursive CTE and Split string function
;WITH data
AS (SELECT org_string,
replace_with,
cs.Item,
cs.ItemNumber
FROM (VALUES ('32.87.65.54.89','87.65' ),
('11.23.45','23' ),
('14.99.16.84','84.14' ),
('11.23.45.65.31.90','23' ),
('34.35.36','35' ),
('34.35.36.76.44.22','35' ),
('34','34' ),
('45.23.11','45.11')) tc (org_string, replace_with)
CROSS apply [Delimitedsplit8k](replace_with, '.') cs),
cte
AS (SELECT org_string,
replace_with,
Item,
Replace('.' + org_string, + '.' + Item, '') AS result,
ItemNumber
FROM data
WHERE ItemNumber = 1
UNION ALL
SELECT d.org_string,
d.replace_with,
d.Item,
CASE
WHEN LEFT(Replace('.' + result, '.' + d.Item, ''), 1) = '.' THEN Stuff(Replace('.' + result, '.' + d.Item, ''), 1, 1, '')
ELSE Replace('.' + result, '.' + d.Item, '')
END,
d.ItemNumber
FROM cte c
JOIN data d
ON c.org_string = d.org_string
AND d.ItemNumber = c.ItemNumber + 1)
SELECT TOP 1 WITH ties org_string,
replace_with,
result = Isnull(Stuff(result, 1, 1, ''), '')
FROM cte
ORDER BY Row_number()OVER(partition BY org_string ORDER BY ItemNumber DESC)
Result :
╔═══════════════════╦══════════════╦════════════════╗
║ org_string ║ replace_with ║ result ║
╠═══════════════════╬══════════════╬════════════════╣
║ 11.23.45 ║ 23 ║ 11.45 ║
║ 11.23.45.65.31.90 ║ 23 ║ 11.45.65.31.90 ║
║ 14.99.16.84 ║ 84.14 ║ 99.16 ║
║ 34 ║ 34 ║ ║
║ 34.35.36 ║ 35 ║ 34.36 ║
║ 34.35.36.76.44.22 ║ 35 ║ 34.36.76.44.22 ║
║ 45.23.11 ║ 45.11 ║ 23 ║
║ 32.87.65.54.89 ║ 87.65 ║ 32.54.89 ║
╚═══════════════════╩══════════════╩════════════════╝
The above code can be converted to a user defined function. I will suggest to create Inline Table valued function instead of Scalar function if you have more records.
Split string Function code referred from http://www.sqlservercentral.com/articles/Tally+Table/72993/
CREATE FUNCTION [dbo].[DelimitedSplit8K]
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover NVARCHAR(4000)
WITH E1(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
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
GO
You could try this..
CREATE FUNCTION StringSpecialReplace(#TargetString VARCHAR(1000),
#InputString VARCHAR(1000))
returns VARCHAR(1000)
AS
BEGIN
declare #result as varchar(1000)
;with CTE1 AS
(
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Certs
FROM (SELECT CAST('<XMLRoot><RowData>' + REPLACE(#TargetString,'.','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
),
CTE2 AS
(
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS Certs
FROM (SELECT CAST('<XMLRoot><RowData>' + REPLACE(#InputString,'.','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
)
SELECT
#Result = CASE
WHEN #Result IS NULL
THEN certs
ELSE #Result + '.' + certs
END
FROM CTE1 where certs not in (select certs from CTE2)
return #Result
END
CREATE FUNCTION MyRemoveFunc (
#OrigString VARCHAR(8000),
#RemovedString VARCHAR(max)
)
RETURNS varchar(max)
BEGIN
DECLARE #xml XML
SET #xml='<n>'+REPLACE(#RemovedString,'.','</n><n>')+'</n>'
SET #OrigString='.'+#OrigString+'.'
SELECT #OrigString=REPLACE(#OrigString,'.'+s.b.value('.','varchar(200)')+'.','.')
FROM #xml.nodes('n')s(b)
RETURN CASE WHEN LEN(#OrigString)>2 THEN SUBSTRING(#OrigString,2,LEN(#OrigString)-2) ELSE '' END
END
SELECT t.*,dbo.MyRemoveFunc(t.o,t.r)
FROM (VALUES ('32.87.65.54.89','87.65' ),
('11.23.45','23' ),
('14.99.16.84','84.14' ),
('11.23.45.65.31.90','23' ),
('34.35.36','35' ),
('34.35.36.76.44.22','35' ),
('34','34' ),
('45.23.11','45.11')) t (o, r)
o r
----------------- ----- -------------------------
32.87.65.54.89 87.65 32.54.89
11.23.45 23 11.45
14.99.16.84 84.14 99.16
11.23.45.65.31.90 23 11.45.65.31.90
34.35.36 35 34.36
34.35.36.76.44.22 35 34.36.76.44.22
34 34
45.23.11 45.11 23
This question already has answers here:
Transpose rows and columns with no aggregate
(2 answers)
Closed 8 years ago.
I have tried viewing other posts on the subject but all the examples I've seen are based on knowing a specific value.
Example of what I have:
Address Name Number
------- ------- -------
1234 Main Bob 555-555-5555
1234 Main Karen 444-444-4444
1990 Maple Susie 333-333-3333
1010 12th Joe 222-222-2222
1010 12th Beth 111-111-1111
1010 12th Steve 444-433-3221
Example of what I want:
Address Contact1 Contact2 Contact3
------- ------- -------- --------
1234 Main Bob:555-555-5555 Karen:444-444-4444 NULL
1990 Maple Susie:333-333-3333 NULL NULL
1010 12th Joe: 222-222-2222 Beth 111-111-1111 Steve 444-433-3221
There are thousands of rows so I can't CASE and.. I'm a more than a little lost here.
Any suggestions?
I think you can use the following query. This assumes you have maximum three contacts
;WITH orderedAddress(Address,Name,Number,Sort)
AS
(
SELECT Address,Name,Number,ROW_NUMBER() OVER(PARTITION BY Address ORDER BY Name) AS num
FROM Addresses
)
SELECT main.Address,Contact1.Name + ':' + Contact1.Number AS Contact1 ,
Contact2.Name + ':' + ISNULL(Contact2.Number,'') AS Contact2 ,
Contact3.Name + ':' + ISNULL(Contact3.Number,'') AS Contact3
FROM
(SELECT DISTINCT Address FROM Addresses) AS main
LEFT JOIN OrderedAddress Contact1 ON main.Address = Contact1.Address AND Contact1.Sort=1
LEFT JOIN OrderedAddress Contact2 ON main.Address = Contact2.Address AND Contact2.Sort=2
LEFT JOIN OrderedAddress Contact3 ON main.Address = Contact3.Address AND Contact3.Sort=3
A dynamic pivot will work but so will a dynamic cross tab. Generally speaking a cross tab will beat the pivot for performance. Here is an example of one I posted just a few days ago.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
exec sp_executesql #SqlToExecute
I want to pivot and join to select from 3 tables
Table 1: INT,VARCHAR,FLOAT
ID Name value
---------------------------
1 a1 32116580
2 a2 50785384
3 a3 54327508
4 a4 61030844
Table 2: INT, VARCHAR, FLOAT
ID Name value
---------------------------
1 x11 61326085092
2 x12 80368184260
3 x13 83023398776
4 x14 91144307692
5 x22 95486535484
6 x23 90357090612
7 x24 100588807668
8 x33 707811916752
9 x34 93128452928
10 x44 84566653668
Table 3: INT, VARCHAR, FLOAT
ID Name value
---------------------------
1 q1 61326085092
2 q2 95486535484
3 q3 707811916752
4 q4 84566653668
output table:
column1 column2 column3 column4
--------------------------------------------------------------------------
a1*a1/(q1+q1+x11) a1*a2/(q1+q2+x12) a1*a3/(q1+q3+x13) a1*a4/(q1+q4+x14)
null a2*a2/(q2+q2+x22) a2*a3/(q2+q3+x23) a2*a4/(q2+q4+x24)
null null a3*a3/(q3+q3+x339 a3*a4/(q3+q4+x34)
null null null a4*a4/(q4+q4+x44)
(I'm putting the 'Name' of the column of the 3 different tables instead of numbers)
How to do this?
I guess I have to do two pivots? and
unpivot?...
Well do not how to complete it..
SELECT *
FROM (
SELECT
t1.a1,
t1.a2,
t2.x,
t3.q
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id
...
) Output
PIVOT (
name IN (
...
PIVOT(name ... )
)
) PivotTable
UPDATE
Previously I have *'s I have changed it to division and sum, the *'s were just an example,
Sample tables
create table Table1(ID int, Name varchar(10), value float)
insert table1 select
1 ,'a1', 32116580 union all select
2 ,'a2', 50785384 union all select
3 ,'a3', 54327508 union all select
4 ,'a4', 61030844
create table Table2(ID int, Name varchar(10), value float)
insert Table2 select
1 ,'x11', 61326085092 union all select
2 ,'x12', 80368184260 union all select
3 ,'x13', 83023398776 union all select
4 ,'x14', 91144307692 union all select
5 ,'x22', 95486535484 union all select
6 ,'x23', 90357090612 union all select
7 ,'x24', 100588807668 union all select
8 ,'x33', 707811916752 union all select
9 ,'x34', 93128452928 union all select
10 ,'x44', 84566653668
create table Table3(ID int, Name varchar(10), value float)
insert Table3 select
1 ,'q1', 61326085092 union all select
2 ,'q2', 95486535484 union all select
3 ,'q3', 707811916752 union all select
4 ,'q4', 84566653668
The query you need, for N = 4. For any other N, just use dynamic SQL to build the query, changing the 2 lines required as indicated by **.
;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
case when col+1>N then row+1 else row end,
case when col+1>N then row+1 else col+1 end,
total, N
from coords
where i<total
)
select [1],[2],[3],[4] -- **, e.g. ,[5],[6],etc
from
(
select
c.row,
c.col,
cellvalue= ar.value*ac.value/(qr.value+qc.value+x.value)
from coords c
inner join table1 ar on ar.id = c.row
inner join table1 ac on ac.id = c.col
inner join table3 qr on qr.id = c.row
inner join table3 qc on qc.ID = c.col
inner join table2 x on x.ID = c.i
) p
pivot (max(cellvalue) for col in ([1],[2],[3],[4])) pv -- **
order by row
Output:
1 2 3 4
---------------------- ---------------------- ---------------------- ----------------------
5606.50338459295 6876.83326310711 2047.51559459649 8269.17991568225
NULL 9003.55641750708 3087.36780924588 11044.2303130135
NULL NULL 1389.95405212248 3744.35614651666
NULL NULL NULL 14681.7678040306
The dynamic version
declare #Sql nvarchar(max)
select #Sql = ISNULL(#sql + ',', '') + QUOTENAME(RIGHT(number,10))
from master..spt_values
where type='P' and number between 1 and (select COUNT(*) From table1)
set #Sql = '
;with coords(i,row,col,total,N) as (
select 1,1,1,N.N*(N.N+1)/2, N.N
from (select count(*) N from table1) N
union all
select i+1,
case when col+1>N then row+1 else row end,
case when col+1>N then row+1 else col+1 end,
total, N
from coords
where i<total
)
select ' + #sql + '
from
(
select
c.row,
c.col,
cellvalue= ar.value*ac.value/(qr.value+qc.value+x.value)
from coords c
inner join table1 ar on ar.id = c.row
inner join table1 ac on ac.id = c.col
inner join table3 qr on qr.id = c.row
inner join table3 qc on qc.ID = c.col
inner join table2 x on x.ID = c.i
) p
pivot (max(cellvalue) for col in (' + #sql + ')) pv
order by row
option (maxrecursion 0) -- ! use with caution
'
exec(#sql)