I have a description field where data is going to look like this:
ID Description Title
1234 serial numbers are *XC54566, AB2345fg, 12IUT456* blahblah
I want to extract everything inside the two asterisk and show them in a column with ',' being the delimiter that differentiates between the serial numbers. The output would then look like
ID Serial_Numbers
1234 XC54566
1234 AB2345fg
1234 12IUT456
Looking for a SQL Server query to extract this information using SELECT and some form of substring/left/right function thingy.
Hopefully a SQL select statement.
If sequence does not matter, how about a string_split() or two ?
Example
Select A.ID
,Serial_Numbers = trim(C.value)
From YourTable A
Cross Apply String_split([Description],'*') B
Cross Apply String_split(B.Value,',') C
Where B.Value like '%,%'
Results
ID Serial_Numbers
1234 XC54566
1234 AB2345fg
1234 12IUT456
at the first I Use XML For retrieve the 2nd element then in the second step, to separate with , from STRING_SPLIT.
yo can use this Query:
select ID,trim(Value)
from(
SELECT ID,CAST('<t>' + REPLACE(Description , '*','</t><t>') + '</t>' AS
XML).value('/t[2]','varchar(50)') as A
FROM T
) as B
CROSS APPLY STRING_SPLIT(A,',')
I used the following table and data for testing
CREATE TABLE [dbo].[T](
[ID] [int] NULL,
[Description] [nvarchar](max) NULL
)
INSERT [dbo].[T] ([ID], [Description])
VALUES (1234, N'serial numbers are *XC54566, AB2345fg, 12IUT456*')
INSERT [dbo].[T] ([ID], [Description])
VALUES (5678, N'serial numbers , are *XC54566, AB2345fg, 12IUT456*')
INSERT [dbo].[T] ([ID], [Description])
VALUES (9784, N'serial numbers are *XC54566*')
//select * from dbo.search('XC54566, AB2345fg, 12IUT456')
CREATE FUNCTION [dbo].[Split]
(
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+1,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
Related
I'm having a table where Assortments are groups containing multiple products (UPC is the productId). I want to verify that the same UPCs are in each Assortment, but I don't know how to do this.
Here is some test data - we're on SQL Server 2014:
--"correct" as each assortment contains the same products
DECLARE #input TABLE (Assortment VARCHAR(100) NOT NULL, UPC VARCHAR(20) NOT NULL);
INSERT INTO #input (Assortment,UPC)
VALUES ('A','1'),('A','2'),('B','1'),('B','2');
--"not correct" as _not_ each assortment contains the same products
DECLARE #input TABLE (Assortment VARCHAR(100) NOT NULL, UPC VARCHAR(20) NOT NULL);
INSERT INTO #input (Assortment,UPC)
VALUES ('A','1'),('A','2'),('B','1'),('B','3');
My first idea was to count the number of products for each Assortment and check if the count is the same for each Assortment and do the same thing the other way round - checking that the number of Assortments a UPC is in is always the same.
But this does not work in a case like this:
DECLARE #input TABLE (Assortment VARCHAR(100) NOT NULL, UPC VARCHAR(20) NOT NULL);
INSERT INTO #input (Assortment,UPC)
VALUES ('A','1'),('A','2'),('B','3'),('B','4');
My next idea was to group by UPC and use a CLR-function to concat all Assortments a UPC is in and compare those for differences, but unfortunately it ignores sort order and by that does not work.
Any suggestions?
This can be solved by using grouping with FOR XML PATH as suggested in the comment from AaronBertrand.
Here is the code I'm using now:
DECLARE #input TABLE (Assortment VARCHAR(100) NOT NULL, UPC VARCHAR(20) NOT NULL);
INSERT INTO #input (Assortment,UPC)
VALUES ('A','1'),('A','2'),('B','1'),('B','2');
DECLARE #grouped TABLE (UPC VARCHAR(100) NOT NULL, Assortments VARCHAR(MAX) NOT NULL)
INSERT INTO #grouped (UPC,Assortments)
SELECT UPC, Assortments = STUFF(
(SELECT N', ' + i2.Assortment
FROM #input AS i2
WHERE i2.UPC = i.upc
ORDER BY
i2.Assortment
FOR XML PATH(N''),
TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 2, N'')
FROM #input AS i
GROUP BY
UPC;
--if this returns something, it is "not correct"
SELECT g.UPC,
g.Assortments
FROM #grouped AS g
WHERE EXISTS (
SELECT g2.Assortments
FROM #grouped AS g2
WHERE g2.UPC <> g.UPC AND
g2.Assortments <> g.Assortments
)
Edit:
After thinking about it again cross joining DISTINCT Assortments WITH DISTINCT UPCs to get all combinations that should be there and then doing a LEFT OUTER JOIN to #input should also work and avoids the string concatenation.
DECLARE #input TABLE (Assortment VARCHAR(100) NOT NULL, UPC VARCHAR(20) NOT NULL);
INSERT INTO #input (Assortment,UPC)
VALUES ('A','1'),('A','2'),('B','3'),('B','4');
;WITH distinct_upcs AS (
SELECT DISTINCT
i.UPC
FROM #input AS i
),
distinct_assortments AS (
SELECT DISTINCT
i.Assortment
FROM #input AS i
),
crossed AS (
SELECT du.UPC,
da.Assortment
FROM distinct_assortments AS da CROSS JOIN
distinct_upcs AS du
)
--if this returns something it is "not correct"
SELECT c.UPC,
c.Assortment
FROM crossed AS c LEFT OUTER JOIN
#input AS i ON
c.UPC = i.UPC AND
c.Assortment = i.Assortment
WHERE i.UPC IS NULL
I want to achieve an auto increment ID with prefix but resetting the number if it has different prefix.
The output I want looks like this:
ID PREFIX PROJECTID
1 PID_ PID_1
2 PID_ PID_2
3 RID_ RID_1
4 RID_ RID_2
But the result I got with my script is this:
ID PREFIX PROJECTID
1 PID_ PID_1
2 PID_ PID_2
3 RID_ RID_3
4 RID_ RID_4
Here's my script to create the table
CREATE TABLE PROJECTS
(ID INT IDENTITY(1,1) NOT NULL,
PREFIX NVARCHAR(10) NOT NULL,
PROJECTID AS ISNULL(PREFIX + CAST(ID AS NVARCHAR(10)), '') PERSISTED)
INSERT INTO PROJECTS(PREFIX) VALUES('PID_'),('PID_'),('RID_'),('RID_')
I'm using MS SQL 2012
you want like this
CREATE TABLE #PROJECTS
(
ID INT IDENTITY(1, 1)
NOT NULL ,
PREFIX NVARCHAR(10) NOT NULL ,
PROJECTID NVARCHAR(11)
)
INSERT INTO #PROJECTS
( PREFIX )
VALUES ( 'PID_' ),
( 'PID_' ),
( 'RID_' ),
( 'RID_' )
suppose you have above data in your table
now if you want to perform insert with DECLARE #PREFIX NVARCHAR(10) = 'RID_'
INSERT INTO #PROJECTS
( PREFIX ,
PROJECTID
)
SELECT #PREFIX ,
#PREFIX + CAST(( COUNT(TT.rn) + 1 ) AS NVARCHAR(1))
FROM ( SELECT ROW_NUMBER() OVER ( PARTITION BY P.PREFIX ORDER BY ( SELECT
NULL
) ) AS rn
FROM #PROJECTS AS P
WHERE P.PREFIX = #PREFIX
) AS tt
see above query may helps you.
Hi i found the ansowr after working couple of hours in Ms Sql server
USE [StocksDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[tb1_triger]
ON [dbo].[Table1]
instead of INSERT
AS
declare #name nchar(12)
select top 1 #name=name from inserted
declare #maxid char(12)
select #maxid = MAX(id1) from Table1
BEGIN
SET NOCOUNT ON;
if (#maxid is null)
begin
set #maxid=0
end
set #maxid= substring(#maxid, 5 , LEN(#maxid))+1
INSERT INTO table1
(id1,name) SELECT CONCAT_WS((REPLICATE('0',12-4-LEN(#maxid))),'tblo',#maxid),i.name
from inserted i
END
You can do this with an INSTEAD OF trigger on the table rather than using a PERSISTED column. I have written the trigger so that it will correctly handle bulk inserts as this is something many people overlook. Also, for my solution it is not necessary to have an IDENTITY column on the table if you do not want it.
So the table has been defined with the column included. Also, you can get rid of the IDENTITY column as I mentioned above:
CREATE TABLE dbo.PROJECTS
(
ID INT IDENTITY(1, 1) NOT NULL,
PREFIX NVARCHAR(10) NOT NULL,
PROJECTID NVARCHAR(20) NOT NULL
);
One note - since the PREFIX column is NVARCHAR(10) and I do not know how big the numbers will get, the size of the PROEJCTID column was increased to prevent overflow. Adjust the size as your data requires.
Here is the trigger:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER dbo.InsertProjects
ON dbo.PROJECTS
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #rowsAffected INT = (SELECT COUNT(*) FROM Inserted);
-- if there are no rows affected, no need to do anything
IF #rowsAffected = 0 RETURN;
DECLARE #ExistingCounts TABLE (
Prefix NVARCHAR(10) NOT NULL,
ExistingCount INT NOT NULL
);
-- get the count for each existing prefix
INSERT INTO #ExistingCounts(Prefix, ExistingCount)
SELECT PREFIX, COUNT(*) FROM dbo.PROJECTS GROUP BY PREFIX;
-- since this is an INSTEAD OF trigger, we must do the insert ourself.
-- a prefix might not exist, so use ISNULL() to get a zero in that case.
INSERT INTO dbo.PROJECTS
(
PREFIX, PROJECTID
)
SELECT sub.PREFIX,
-- the number after the prefix is the existing count for the prefix plus
-- the position of the prefix in the Inserted table
sub.PREFIX + CAST((sub.ExistingCount + sub.Number) AS NVARCHAR(10))
FROM
(SELECT i.PREFIX,
-- get the position (1, 2, 3...) of the prefix in the Inserted table
ROW_NUMBER() OVER(PARTITION BY i.PREFIX ORDER BY i.PREFIX) AS [Number],
-- get the existing count of the prefix
ISNULL(c.ExistingCount, 0) AS [ExistingCount]
FROM Inserted AS i
LEFT OUTER JOIN #ExistingCounts AS c ON c.Prefix = i.PREFIX) AS sub;
END
GO
I have included comments in the source code to explain the simple logic. Hopefully this helps and is what you are looking for :-)
Hey use this query..
CREATE FUNCTION DBO.GET_NEX_P_ID(#PREF VARCHAR(4))
RETURNS NVARCHAR(24)
AS
BEGIN
RETURN (SELECT #PREF+CAST(COUNT(1)+1 AS VARCHAR) FROM PROJECTS WHERE PREFIX=#PREF)
END
GO
CREATE TABLE PROJECTS
(
PREFIX VARCHAR(8),
PROJECTID NVARCHAR(24)
)
GO
INSERT INTO PROJECTS
VALUES('PRJ_',DBO.GET_NEX_P_ID('PRJ_'))
GO
INSERT INTO PROJECTS
VALUES('PRQ_',DBO.GET_NEX_P_ID('PRQ_'))
GO
Thanks
DECLARE #ID INT
SET #ID = (select top 1 USER_REQ_JOB_ID
from T8504_USER_REQ_JOB
where JOB_GRP_ID = 160
order by LST_UPDT_TS desc)
SELECT INPUT_PARM_VAL_TX
from TBL_RPT_JOB_INPUT_PARAM
where USER_REQ_JOB_ID = #ID
This returns these results:
USA
USCC
6
7
2
These five records what I get I want to assign to five different variables to use in stored procedure.
I was trying with table variable like this :
declare #CID table (
Region Char(3)
,Segment Char(3)
,MasterContractId int
,ctcid int
,templateid int)
insert into #CID (Region,Segment,MasterContractId,ctcid,templateid)
But how to insert that 5 rows here?
INSERT INTO #CID
select * from
(
select
'Temp' + convert(char(1), row_number() over (order by (select 0))) as columnName,
INPUT_PARM_VAL_TX as Value
from TBL_RPT_JOB_INPUT_PARAM where USER_REQ_JOB_ID = #ID
) d
pivot
(
max(value)
for columnname in (Temp1, Temp2, Temp3, Temp4, Temp5)
) piv;
See if this helps.
Take a look at this fiddle for an example.
Courtesy:
Add row number to this T-SQL query
Efficiently convert rows to columns in sql server
EDIT: The sql adds an extra column to generate row numbers to use it as an extra column, which is pivoted as column heading.
it's really gross, but one way you could probably do it is this (though you'll need to apply it to your case):
http://sqlfiddle.com/#!6/d41d8/21507
declare #table TABLE (value varchar(50))
INSERT INTO #table
VALUES ('first')
INSERT INTO #table
VALUES ('second')
INSERT INTO #table
VALUES (3)
INSERT INTO #table
VALUES (4)
DECLARE #temp TABLE (id int identity(1,1), value varchar(50))
INSERT INTO #temp
SELECT [value]
FROM #table t
SELECT *
FROM #temp
DECLARE #CID TABLE (Region varchar(50), cont varchar(50), another int, andAnother int)
INSERT INTO #CID
(
Region,
cont,
another,
andAnother
)
VALUES
(
(SELECT value FROM #temp WHERE id = 1), -- Region - varchar
(SELECT value FROM #temp WHERE id = 2), -- cont - varchar
(SELECT value FROM #temp WHERE id = 3), -- another - int
(SELECT value FROM #temp WHERE id = 4) -- andAnother - int
)
SELECT * FROM #cid
note that i assumed you're using mssql, you did not specify
I have a table
CREATE TABLE [StudentsByKindergarten]
(
[FK_KindergartenId] [int] IDENTITY(1,1) NOT NULL,
[StudentList] [nvarchar]
)
where the entries are
(1, "John, Alex, Sarah")
(2, "")
(3, "Jonny")
(4, "John, Alex")
I want to migrate this information to the following table.
CREATE TABLE [KindergartenStudents]
(
[FK_KindergartenId] [int] NOT NULL,
[StudentName] [nvarchar] NOT NULL)
)
so that it will have
(1, "John")
(1, "Alex")
(1, "Sarah")
(3, "Jonny")
(4, "John")
(4, "Alex")
I think I can achieve split function using something like the answer here: How do I split a string so I can access item x?
Using the function here:
http://www.codeproject.com/Articles/7938/SQL-User-Defined-Function-to-Parse-a-Delimited-Str
I can do something like this,
INSERT INTO [KindergartenStudents] ([FK_KindergartenId], [Studentname])
SELECT
sbk.FK_KindergartenId,
parsed.txt_value
FROM
[StudentsByKindergarten] sbk, dbo.fn_ParseText2Table(sbk.StudentList,',') parsed
GO
but doesn't seem to work.
Based on this question, I've learned a better approach for this problem. You just need to use CROSS APPLY with your suggested function fn_ParseText2Table.
Sample Fiddle
INSERT INTO KindergartenStudents
(FK_KindergartenId, StudentName)
SELECT
sbk.FK_KindergartenId,
parsed.txt_value
FROM
StudentsByKindergarten sbk
CROSS APPLY
fn_ParseText2Table(sbk.StudentList, ',') parsed
I've used the function that you suggested (fn_ParseText2Table) and the following T-SQL is working. You can test it with this fiddle: link.
BEGIN
DECLARE
#ID int,
#iterations int
-- Iterate the number of not empty rows
SET #iterations =
(SELECT
COUNT(*)
FROM
StudentsByKindergarten
WHERE
DATALENGTH(StudentList) > 0
)
WHILE ( #iterations > 0 )
BEGIN
-- Select the ID of row_number() = #iteration
SET #ID =
(SELECT
FK_KindergartenId
FROM
(SELECT
*,
ROW_NUMBER() OVER (ORDER BY FK_KindergartenId DESC) as rn
FROM
StudentsByKindergarten
WHERE
DATALENGTH(StudentList) > 0) rows
WHERE
rows.rn = #iterations
)
SET #iterations -= 1
-- Insert the parsed values
INSERT INTO KindergartenStudents
(FK_KindergartenId, StudentName)
SELECT
#ID,
parsed.txt_value
FROM
fn_ParseText2Table
(
(SELECT
StudentList
FROM
StudentsByKindergarten
WHERE
FK_KindergartenId = #ID),
',') parsed
END
END
I want to write a stored procedure like this
Create Proc dbo.GetApplicantsByIDs
as
Select * from Applicants where ID in (1,2,3,4)
How i can pass 1,2,3 as parameters and these ids may be multiple.
You can send your id's as XML to the SP.
create procedure dbo.GetApplicantsByIDs
#IDList xml
as
-- Table to hold the id's
declare #IDs table(ID int primary key)
-- Fill table with id's
insert into #IDs(ID)
select X.ID.value('.', 'int')
from #IDList.nodes('/i') as X(ID)
select *
from Applicants
where ID in (select ID
from #IDs)
The parameter string should look like this:
'<i>1</i><i>2</i><i>3</i>'
I think there is a better solution.
You can create a function like:
CREATE FUNCTION [dbo].[Split] (#sep char(1), #s varchar(8000))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, [stop]) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, [stop] + 1, CHARINDEX(#sep, #s, [stop] + 1)
FROM Pieces
WHERE [stop] > 0
)
SELECT pn as [index],
SUBSTRING(#s, start, CASE WHEN [stop] > 0 THEN [stop]-start ELSE 8000 END) AS value
FROM Pieces
)
Then you can get the result from for input data '20,10,15,18,19'
SELECT * FROM [dbo].[Split](',', '20,10,15,18,19')
The result will be:
index value
1 20
2 10
3 15
4 18
5 19
And I can rewrite your procedure as below:
Create Proc dbo.GetApplicantsByIDs
#Ids NVARCHAR(MAX)
as
Select * from Applicants where ID in
(SELECT value FROM [dbo].[Split](',', #Ids)
Another solution using table variables (for the sake of it):
if exists (select table_name from information_schema.tables where table_name = 'Applicants')
drop table Applicants
go
create table Applicants (
Id int identity,
Name varchar(50)
)
go
insert Applicants (Name) values ('David')
insert Applicants (Name) values ('John')
insert Applicants (Name) values ('Scott')
insert Applicants (Name) values ('Anna')
insert Applicants (Name) values ('Esther')
go
create type IDs as table (
ID int
)
go
if exists (select routine_name from information_schema.routines where routine_name = 'GetApplicantsByIDs')
drop proc GetApplicantsByIDs
go
create proc GetApplicantsByIDs (
#IDs IDs readonly
)
as
begin
select * from Applicants A where Id in (select * from #IDs)
end
go
declare #MyIDs as IDs
insert #MyIDs values (2)
insert #MyIDs values (4)
insert #MyIDs values (1)
exec GetApplicantsByIDs #MyIDs
go
Produces:
1 David
2 John
4 Anna