Can't figure out how to use XML path - sql-server

Hi I have tried all I can, but can't figure out how to do this.
I have this table "T1"
----RID----PID----
1 1
1 2
which i left join on RID with another "T2" to get the name column of PID:
----RID-----PID-----Pname----
1 1 Task1
1 2 Task2
So far so good. But what I really want is at table with these columns: 1. (distinct RID) 2.(A count of PID's in each Rid) 3.( Comma-seperated string of all PID-names)
----RID-----PID-----Pname----
1 2 Task1,Task2
But if a RID only has one Pname then here should be no comma:
----RID-----PID-----Pname----
1 2 Task1,Task2
2 1 Task1
Anyone ? I have tried with XML PATH but can't figure it out.....

Full working example:
DECLARE #T1 TABLE
(
[RID] INT
,[PID] INT
);
INSERT INTO #T1 ([RID], [PID])
VALUES (1, 1)
,(1, 2);
DECLARE #T2 TABLE
(
[RID] INT
,[PID] INT
,[Pname] VARCHAR(12)
);
INSERT INTO #T2 ([RID], [PID], [Pname])
VALUES (1, 1, 'Task1')
,(1, 2, 'Task2');
SELECT T1.[RID]
,COUNT(T1.[PID]) AS [PID]
,[value] AS [PName]
FROM #T1 T1
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + T2.[Pname]
FROM #T2 T2
WHERE T2.[RID] = T2.[RID]
ORDER BY T2.[PID]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) DS ([value])
GROUP BY T1. [RID]
,[value];

Related

How to SELECT specific characters using SQL in SQL server database

How can I select only information that start with a #?
I have a table with 5 columns and in one of the columns i.e. the comments column there is information like:
#2345 Changed by Mark
Paul changed ticket number #5923
Someone changed ticket number #5823 and #9333
#3555 is missing from the list, can only see #5789, #9000 and #4568
In the sample of 4 rows above, I want my select statement to return only ticket numbers as shown below:
comments
#2345
#5923
#5823, #9333
#5789, #9000, #4568
Someone said regular expressions can do the work for me but I am fresh graduate and have never seen such before. Can you help me please??
Select the required fields from your database table using SQL, then perform regex operations on the result in another language such as c++, php etc. when outputting your result to the client
First Create Function To extract #numeric values
CREATE FUNCTION dbo.udf_GetNumeric
(
#strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256
)
AS
BEGIN
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^#0-9]%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^#0-9]%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
GO
By Using Below Code we can Extract the numeric values
Declare #TempTable TABLE(ID Int Identity,Value varchar(1000))
INSERT INTO #TempTable
SELECT '#2345 Changed by Mark'
UNION ALL SELECT 'Paul changed ticket number #5923'
UNION ALL SELECT 'Someone changed ticket number #5823 and #9333'
UNION ALL SELECT '#3555 is missing from the list, can only see #5789, #9000 and #4568'
SELECT ID,
RIGHT(LTRIM(REPLACE(Value,'#',' ,#')),LEN(RTRIM(LTRIM(REPLACE(Value,'#',' ,#'))))-1)AS Value FROM
(
SELECT ID,dbo.udf_GetNumeric(Value) AS Value From #TempTable
)Dt
If you need to extract Numeric values Instead of pre-fixed wirh '#' Symbol
SELECT ID,
REPLACE(RIGHT(LTRIM(REPLACE(Value,'#',' ,#')),LEN(RTRIM(LTRIM(REPLACE(Value,'#',' ,#'))))-1),'#',' ')AS Value FROM
(
SELECT ID,dbo.udf_GetNumeric(Value) AS Value From #TempTable
)Dt
OutPut
ID Value
----------
1 #2345
2 #5923
3 #5823 ,#9333
4 #3555 ,#5789 ,#9000 ,#4568
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
Drop table #TempTable
CREATE TABLE [dbo].#TempTable(
[ID] [int] NOT NULL,
[Value] [varchar](1000) NULL
) ON [PRIMARY]
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (1, N'#2345 Changed by Mark #111111,767677,33333,#5656 vbvb')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (2, N'Paul changed ticket number #5923,5555555 464646 #010101,5555544rrr,wwww AND #4 ')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (3, N'Someone changed ticket number #5823 and #9333,7777')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (4, N'#3555 is missing from the list, can only see #5789, #9000 and #4568')
GO
;WITH cte
AS
(
SELECT ID,Split.a.value('.', 'VARCHAR(1000)') AS TablesData
FROM (
SELECT ID,CAST('<S>' + REPLACE(Value, ' ', '</S><S>') + '</S>' AS XML) AS TablesData
FROM #TempTable
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)
,Final
AS
(
SELECT ID,TablesData FROM
(
SELECT ID, Split.a.value('.', 'VARCHAR(1000)') AS TablesData,CHARINDEX('#',Split.a.value('.', 'VARCHAR(1000)')) AS Data2
FROM (
SELECT ID,CAST('<S>' + REPLACE(TablesData, ',', '</S><S>') + '</S>' AS XML) AS TablesData
FROM cte
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)DT
WHERE dt.Data2 <>0
)
SELECT DISTINCT ID
,STUFF((
SELECT ', ' + CAST(TablesData AS VARCHAR(50))
FROM Final i
WHERE i.ID = o.ID
FOR XML PATH('')
), 1, 1, '') AS ColumnsWith#ValuesOnly
FROM Final o
ORDER BY 1 ASC
OutPut
ID ColumnsWith#ValuesOnly
---------------------------
1 #2345, #111111, #5656
2 #5923, #010101, #4
3 #5823, #9333
4 #3555, #5789, #9000, #4568
The best way is to use this script below:
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
Drop table #TempTable
CREATE TABLE [dbo].#TempTable(
[ID] [int] NOT NULL,
[Value] [varchar](1000) NULL
) ON [PRIMARY]
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (1, N'#2345 Changed by Mark #111111,767677,33333,#5656 vbvb')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (2, N'Paul changed ticket number #5923,5555555 464646 #010101,5555544rrr,wwww AND #4 ')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (3, N'Someone changed ticket number #5823 and #9333,7777')
GO
INSERT [dbo].#TempTable ([ID], [Value]) VALUES (4, N'#3555 is missing from the list, can only see #5789, #9000 and #4568')
GO
;WITH cte
AS
(
SELECT ID,Split.a.value('.', 'VARCHAR(1000)') AS TablesData
FROM (
SELECT ID,CAST('<S>' + REPLACE(Value, ' ', '</S><S>') + '</S>' AS XML) AS TablesData
FROM #TempTable
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)
,Final
AS
(
SELECT ID,TablesData FROM
(
SELECT ID, Split.a.value('.', 'VARCHAR(1000)') AS TablesData,CHARINDEX('#',Split.a.value('.', 'VARCHAR(1000)')) AS Data2
FROM (
SELECT ID,CAST('<S>' + REPLACE(TablesData, ',', '</S><S>') + '</S>' AS XML) AS TablesData
FROM cte
) AS A
CROSS APPLY TablesData.nodes('/S') AS Split(a)
)DT
WHERE dt.Data2 <>0
)
SELECT DISTINCT ID
,STUFF((
SELECT ', ' + CAST(TablesData AS VARCHAR(50))
FROM Final i
WHERE i.ID = o.ID
FOR XML PATH('')
), 1, 1, '') AS ColumnsWith#ValuesOnly
FROM Final o
ORDER BY 1 ASC

How to get id's of parent ids for inserting children

I have Parent and Child table.
The goal is to duplicate the records, except with new primary keys.
Original Tables
Parent(id)
1
Child(id,parentId, data)
1,1
2,1
After insert:
Parent
1
2
Child
1,1
2,1
3,2
4,2
How do I do that? The part I am having trouble with is getting the new parent key for use with the child records.
This is what I have come up with so far.
--DECLARE VARS
declare #currentMetadataDocumentSetId int = 1, --Ohio
#newMetadataDocumentSetid int = 3; --PA
--CLEANUP
IF OBJECT_ID('tempdb..#tempFileRowMap') IS NOT NULL
/*Then it exists*/
DROP TABLE #tempFileRowMap
--Remove existing file row maps.
delete from file_row_map where metadata_document_set_id = #newMetadataDocumentSetid;
--Create a temptable to hold data to be copied.
Select [edi_document_code],
[functional_group],
[description],
3 as [metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set],
file_row_map_id as orig_file_row_map_id
into #tempFileRowMap
from file_row_map fileRowMap
where metadata_document_set_id = #currentMetadataDocumentSetId;
--Select * from #tempFileRowMap;
Insert into file_row_map select
[edi_document_code],
[functional_group],
[description],
[metadata_document_set_id],
[document_name],
[incoming_file_row_subtype],
[metadata_document_id],
[document_subcode],
[outgoing_file_row_subtype],
[asi_type_code],
[asi_action_code],
[metadata_document_set]
from #tempFileRowMap
--Show Results
Select * from file_row_map fileRowMap where fileRowMap.metadata_document_set_id = #newMetadataDocumentSetid
--Update Detail
Select
[file_row_map_id],
[file_row_column],
[element_code],
[element_metadata_id],
[col_description],
[example],
[translate],
[is_used],
[is_mapped],
[page_num],
[subcode],
[qualifier],
[loop_code],
[loop_subcode],
[default_value],
[delete_flag]
into #tempFileRowMapDetail
from [dbo].[file_row_map_detail] d
left join #tempFileRowMap m
on m.orig_file_row_map_id = d.file_row_map_id
select * from #tempFileRowMapDetail
Simply use OUTPUT clause for getting exact Parent Table Primary Key values.
Lets build Example Schema for your case
--For Capturing inserted ID
CREATE TABLE #ID_CAPTURE (PARENT_ID INT,ORDER_NME VARCHAR(20));
--Your Intermidiate Data To insert into Actual Tables
CREATE TABLE #DUMMY_TABLE (ORDER_NME VARCHAR(20), ITEM_NME VARCHAR(20));
--Actual Tables
CREATE TABLE #ORDER_PARENT (ORDER_ID INT IDENTITY,ORDER_NME VARCHAR(20))
CREATE TABLE #ORDER_CHILD (CHILD_ID INT IDENTITY ,ORDER_ID INT, ORDER_NME VARCHAR(20))
INSERT INTO #DUMMY_TABLE
SELECT 'BILL1','Oil'
UNION ALL
SELECT 'BILL1', 'Gas'
UNION ALL
SELECT 'BILL2', 'Diesel'
Now do Inserts in Parent & Child Tables
INSERT INTO #ORDER_PARENT
OUTPUT inserted.ORDER_ID, inserted.ORDER_NME into #ID_CAPTURE
SELECT DISTINCT ORDER_NME FROM #DUMMY_TABLE
INSERT INTO #ORDER_CHILD
SELECT C.PARENT_ID, ITEM_NME FROM #DUMMY_TABLE D
INNER JOIN #ID_CAPTURE C ON D.ORDER_NME = C.ORDER_NME
SELECT * FROM #ID_CAPTURE
SELECT * FROM #ORDER_CHILD
There are other ways to get Inserted Identity values.
See documentation ##IDENTITY (Transact-SQL) , SCOPE_IDENTITY
Try following approach:
DECLARE #Table1 TABLE (
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL, -- FK
[Desc] VARCHAR(50) NOT NULL
);
INSERT #Table1 (ID, ParentID, [Desc])
VALUES
(1, NULL, 'A'),
(2, 1, 'AA.1'),
(3, 1, 'AA.2'),
(4, NULL, 'B'),
(5, 4, 'BB.1'),
(6, 4, 'BB.2'),
(7, 4, 'BB.3'),
(8, 7, 'BBB.1');
DECLARE #ParentID INT = 4;
DECLARE #LastID INT = (SELECT TOP(1) ID FROM #Table1 x ORDER BY x.ID DESC)
IF #LastID IS NULL
BEGIN
RAISERROR('Invalid call', 16, 1)
--RETURN ?
END
SELECT #LastID AS LastID;
/*
LastID
-----------
8
*/
DECLARE #RemapIDs TABLE (
OldID INT NOT NULL PRIMARY KEY,
[NewID] INT NOT NULL UNIQUE
);
WITH CteRecursion
AS (
SELECT 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
WHERE crt.ID = #ParentID
UNION ALL
SELECT cld.Lvl + 1 AS Lvl, crt.ID, crt.ParentID --, crt.[Desc]
FROM #Table1 crt
JOIN CteRecursion cld ON crt.ParentID = cld.ID
)
INSERT #RemapIDs (OldID, [NewID])
SELECT r.ID, #LastID + ROW_NUMBER() OVER(ORDER BY r.Lvl) AS [NewID]
FROM CteRecursion r;
--INSERT #Table1 (ID, ParentID, [Desc])
SELECT nc.[NewID] AS ID, np.[NewID] AS ParentID, o.[Desc]
FROM #Table1 o -- old
JOIN #RemapIDs nc /*new child ID*/ ON o.ID = nc.OldID
LEFT JOIN #RemapIDs np /*new parent ID*/ ON o.ParentID = np.OldID
/*
ID ParentID Desc
----------- ----------- --------------------------------------------------
9 NULL B
10 9 BB.1
11 9 BB.2
12 9 BB.3
13 12 BBB.1
*/
Note: with some minor changes should work w. many ParentIDs values.

query for retrieving information from 3 tables in sql?

i have 3 tables
1)
news_table
nid tilte
1 Hi
2 Hello
2)
Product_table
pid name
2 abc
3 def
4 rty
5 zxc
6 poj
7 lkj
3)
temp
nid pid
1 2
1 3
1 4
2 5
2 6
2 7
I want output like this
nid pids names title
1 2,3,4 abc,def,rtj hi
2 5,6,7 zxc,poj,lkj hello
This a full working example run on SQL Server 2012:
DECLARE #news_table TABLE
(
[nid] TINYINT
,[title] VARCHAR(8)
);
INSERT INTO #news_table ([nid], [title])
VALUES (1, 'Hi')
,(2, 'Hellow');
DECLARE #Product_table TABLE
(
[pid] TINYINT
,[name] VARCHAR(8)
);
INSERT INTO #Product_table ([pid], [name])
VALUES (2, 'abc')
,(3, 'def')
,(4, 'rty')
,(5, 'zxc')
,(6, 'poj')
,(7, 'lkj');
DECLARE #temp TABLE
(
[nid] TINYINT
,[pid] TINYINT
);
INSERT INTO #temp ([nid], [pid])
VALUES (1, 2)
,(1, 3)
,(1, 4)
,(2, 5)
,(2, 6)
,(2, 7);
SELECT [nid]
,[pids]
,[names]
,[title]
FROM #news_table NT
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + PT.[name]
FROM #temp T
INNER JOIN #Product_table PT
ON T.[pid] = PT.[pid]
WHERE T.[nid] = NT.[nid]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
)
) DS1 ([names])
CROSS APPLY
(
SELECT STUFF
(
(
SELECT ',' + CAST(T.[pid] AS VARCHAR(12))
FROM #temp T
WHERE T.[nid] = NT.[nid]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
) DS2 ([pids]);
I will strongly recommend to check the following links about .net integration in SQL Server:
String Utility Functions Sample - full working examples
Stairway to SQLCLR - still in progress
Introduction to SQL Server CLR Integration - official documentation
You can defined your own aggregates. For example, for concatenating strings and the solution will be far more simple:
SELECT NT.[nid]
,[dbo].[Concatenate] (T.[pid]) AS [pids]
,[dbo].[Concatenate] ([name]) AS [names]
,NT.[title]
FROM #news_table NT
INNER JOIN #temp T
ON T.[nid] = NT.[nid]
INNER JOIN #Product_table PT
ON T.[pid] = PT.[pid]
GROUP BY NT.[nid]
,NT.[title]

How do I find records out of order - SQL?

Let's say I have a table with an ID Identity column, some data, and a datestamp. Like this:
1 data 5/1/2013 12:30
2 data 5/2/2013 15:32
3 data 5/2/2013 16:45
4 data 5/3/2013 9:32
5 data 5/5/2013 8:21
6 data 5/4/2013 9:36
7 data 5/6/2013 11:42
How do I write a query that will show me the one record that is timestamped 5/4? The table has millions of records. I've done some searching, but I don't know what to call what I'm searching for. :/
declare #t table(id int, bla char(4), timestamp datetime)
insert #t values
(1,'data','5/1/2013 12:30'),
(2,'data','5/2/2013 15:32'),
(3,'data','5/2/2013 16:45'),
(4,'data','5/3/2013 9:32'),
(5,'data','5/5/2013 8:21'),
(6,'data','5/4/2013 9:36'),
(7,'data','5/6/2013 11:42')
select timestamp
from
(
select rn1 = row_number() over (order by id),
rn2 = row_number() over (order by timestamp), timestamp
from #t
) a
where rn1 not in (rn2, rn2-1)
in 2008 r2, this would be a way
DECLARE #Table AS TABLE
(id INT , ladate DATETIME)
INSERT INTO #Table VALUES (1, '2013-05-01')
INSERT INTO #Table VALUES (2, '2013-05-02')
INSERT INTO #Table VALUES (3, '2013-05-03')
INSERT INTO #Table VALUES (4, '2013-05-05')
INSERT INTO #Table VALUES (5, '2013-05-04')
INSERT INTO #Table VALUES (6, '2013-05-06')
INSERT INTO #Table VALUES (7, '2013-05-07')
INSERT INTO #Table VALUES (8, '2013-05-08')
--I added the records in the sort order but if not just make sure you are sorted in the query
SELECT t2.ladate FROM #Table T1
INNER JOIN #Table T2 ON T1.Id = T2.Id + 1
INNER JOIN #Table t3 ON t2.id = t3.id + 1
WHERE t3.ladate < t2.ladate AND t2.ladate > t1.ladate
-- I made the assumption that your Id are all there, 1,2,3,4,5.... none missing... if there are rownumbers missing, you can use row_number()

How to add two text row on same column in T-SQL

How can I add two text row on same column, or any alternative aggregate function which can apply sum() on text columns.
id Name
1 A
1 B
2 C
group by id
result like this.
id Names
1 A,B
2 C
Try this:
declare #t table(id int, name varchar(50))
insert #t values(1, 'A')
insert #t values(1, 'B')
insert #t values(2, 'C')
select t.id,
,STUFF((
select ',' + [name]
from #t t1
where t1.id = t.id
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [Names]
from #t t
group by t.id
Result:
id Names
----------- --------------
1 A,B
2 C

Resources