I have a complex Id in my Data Base table Like is 00-000-000.
All names store in one field
01-000-000=Warehouse
01-001-000-=Rack
01-001-001=Bin cart
into the same table. I want to segregate data in 3 different fields. Is it possible in SQL?
there is another method with parsename function
select PARSENAME(replace(left(FieldName,10),'-','.'),3) col1,
PARSENAME(replace(left(FieldName,10),'-','.'),2) col2,
PARSENAME(replace(left(FieldName,10),'-','.'),1) col3 from yourTable
As we don't have quite enough information I am assuming that you wish to split the 3 number identifier at the start of each row into the 3 separate numbers...
If the Ids are always of fixed length (i.e. 2 letters then a dash then 3 letters then a dash then 3 more letters), you can use the substring function to break them out;
WITH TestData as (
SELECT '01-000-000=Warehouse' AS Id
UNION
SELECT '01-001-000-=Rack' AS Id
UNION
SELECT '01-001-001=Bin cart' AS Id
)
SELECT
Id,
substring(Id, 0, 2) AS FirstId,
substring(Id, 4, 3) AS SecondId,
substring(Id, 8, 3) AS ThirdId,
substring(Id, 11, len(id) - 10) AS RestOfString
FROM TestData
If they are variable lengths you will have to use something like the CHARINDEX function to find the positions of the dashes, and then split on them.
If it's always going to be the same length then you can do some simple code using LEFT and RIGHT
Test Data;
IF OBJECT_ID('tempdb..#TestData') IS NOT NULL DROP TABLE #TestData
GO
CREATE TABLE #TestData (FieldName varchar(50))
INSERT INTO #TestData (FieldName)
VALUES
('01-000-000=Warehouse')
,('01-001-000-=Rack')
,('01-001-001=Bin cart')
Query;
SELECT
FieldName
,LEFT(FieldName,2) Result1
,RIGHT(LEFT(FieldName,6),3) Result2
,RIGHT(LEFT(FieldName,10),3) Result3
FROM #TestData
Result;
FieldName Result1 Result2 Result3
01-000-000=Warehouse 01 000 000
01-001-000-=Rack 01 001 000
01-001-001=Bin cart 01 001 001
Use this for dynamic value with 2 "-" sign.
SELECT SUBSTRING('001-0011-0010',1,CHARINDEX('-','001-0011-0010')-1) COLA,
SUBSTRING ('001-0011-0010',
CHARINDEX('-','001-0011-0010')+1,
CHARINDEX('-','001-0011-0010',
CHARINDEX('-','001-0011-0010')+1)-(CHARINDEX('-','001-0011-0010')+1)
) COLB,
SUBSTRING ('001-0011-0010',(CHARINDEX('-','001-0011-0010',
CHARINDEX('-','001-0011-0010')+1))+1, LEN('001-0011-0010')
) COLC
Thanks
Related
I have the following values in the SQL Server table:
But I need to build query from which output look like this:
I know that I should probably use combination of substring and charindex but I have no idea how to do it.
Could you please help me how the query should like?
Thank you!
Try the following, it may work.
SELECT
offerId,
cTypes
FROM yourTable AS mt
CROSS APPLY
EXPLODE(mt.contractTypes) AS dp(cTypes);
You can use string_split function :
select t.offerid, trim(translate(tt.value, '[]"', ' ')) as contractTypes
from table t cross apply
string_split(t.contractTypes, ',') tt(value);
The data in each row in the contractTypes column is a valid JSON array, so you may use OPENJSON() with explicit schema (result is a table with columns defined in the WITH clause) to parse this array and get the expected results:
Table:
CREATE TABLE Data (
offerId int,
contractTypes varchar(1000)
)
INSERT INTO Data
(offerId, contractTypes)
VALUES
(1, '[ "Hlavni pracovni pomer" ]'),
(2, '[ "ÖCVS", "Staz", "Prahovne" ]')
Table:
SELECT d.offerId, j.contractTypes
FROM Data d
OUTER APPLY OPENJSON(d.contractTypes) WITH (contractTypes varchar(100) '$') j
Result:
offerId contractTypes
1 Hlavni pracovni pomer
2 ÖCVS
2 Staz
2 Prahovne
As an additional option, if you want to return the position of the contract type in the contractTypes array, you may use OPENJSON() with default schema (result is a table with columns key, value and type and the value in the key column is the 0-based index of the element in the array):
SELECT
d.offerId,
CONVERT(int, j.[key]) + 1 AS contractId,
j.[value] AS contractType
FROM Data d
OUTER APPLY OPENJSON(d.contractTypes) j
ORDER BY CONVERT(int, j.[key])
Result:
offerId contractId contractType
1 1 Hlavni pracovni pomer
2 1 ÖCVS
2 2 Staz
2 3 Prahovne
I am trying to split a string that has 3 set Alpha Characters that can appear in any order followed by a numeric value. The issue I am having is that the order of the alpha characters isn't fixed. And neither is the number of numeric values after the alpha character it may contain any of the following examples:
X1Y45Z1
Y25Z1
X1Y9Z1
X2Z6
With a a lot of help from our local IT ( I am still learning SQL) I have managed to separate out X Y and Z into separate columns with the numbers after them, but they don't always appear in order
Col1 may contain X or Y
Col2 may contain Y or Z
Col3 may contain Z or nothing
I am trying to get a result like the following:
If X is in Col1, Show number(s) after X, in new column "X", if Y is in col1, Show number(s) after Y in new column "Y", etc.
At present we are using 2 cte's to break up the string. and I am trying to simplify it so that I can search the string, have 3 columns after created 'X','Y','Z' and put the correct number(s) after each Alpha delimiter into it. I should note I Do Not have full admin access so I cannot create new tables or update/insert data or clean it.
Also apologies if this is slightly formatted incorrectly. It is my first post on StackOverflow
declare #tbl table
(
Col1 varchar(100), <-------This Column contains the values I want
)
insert into #tbl
select Col1,
from table1,
where xyz
;with cte as
(
select
Col1,
replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(**Col1**,'P', '</x><x>P'),'C', '</x><x>C'),'I', '</x><x>I'),'M', '</x><x>M'),'S', '</x><x>S'),'Q', '</x><x>Q'),'L', '</x><x>L'),'T', '</x><x>T'),'E', '</x><x>E'),'R', '</x><x>R'),'U', '</x><x>U'),'W', '</x><x>W')
**Col1NODES**
from
#tbl
)
, cte2 (Col1, Col1Nodes) as
(
select
Col1,
convert(xml,'<z><x>' + Col1nodes + '</x></z>') **Col1NODES**
from
cte
)
select
Col1,
isnull(Col1Nodes.value('/z[1]/x[2]','varchar(100)'),'-') F1,
isnull(Col1Nodes.value('/z[1]/x[3]','varchar(100)'),'-') F2,
isnull(Col1Nodes.value('/z[1]/x[4]','varchar(100)'),'-') F3
from
cte2
Current output is below:
If you have SQL Server 2016+ you may try to use the following solution, based on JSON. The important part is to transform the input data into a valid JSON object (X1Y45Z1 is transformed into {"X":1,"Y":45,"Z":1} for example). After that you need to parse this object with OPENJSON() function using the appropriate WITH clause to define the columns in the output.
Table:
CREATE TABLE Data (
TextData nvarchar(100)
)
INSERT INTO Data
(TextData)
VALUES
('X1Y45Z1'),
('Y25Z1'),
('X1Y9Z1'),
('X2Z6'),
('Z1X6')
Statement:
SELECT d.TextData, j.*
FROM Data d
CROSS APPLY OPENJSON(
CONCAT(
N'{',
STUFF(REPLACE(REPLACE(REPLACE(d.TextData, N'X', N',"X":'), N'Y', N',"Y":'), N'Z', N',"Z":'), 1, 1, N''),
N'}'
)
) WITH (
X int '$.X',
Y int '$.Y',
Z int '$.Z'
) j
Output:
---------------------
TextData X Y Z
---------------------
X1Y45Z1 1 45 1
Y25Z1 25 1
X1Y9Z1 1 9 1
X2Z6 2 6
Z1X6 6 1
For versions before SQL Server 2016, you may use an XML based approach. You need to transform text data into an appropriate XML (X1Y45Z1 is transformed into <row><name>X</name><value>1</value></row><row><name>Y</name><value>45</value></row><row><name>Z</name><value>1</value></row> for example):
SELECT
TextData,
XmlData.value('(/row[name = "X"]/value/text())[1]', 'nvarchar(4)') AS X,
XmlData.value('(/row[name = "Y"]/value/text())[1]', 'nvarchar(4)') AS Y,
XmlData.value('(/row[name = "Z"]/value/text())[1]', 'nvarchar(4)') AS Z
FROM (
SELECT
TextData,
CONVERT(
xml,
CONCAT(
STUFF(REPLACE(REPLACE(REPLACE(d.TextData, N'X', N'</value></row><row><name>X</name><value>'), N'Y', N'</value></row><row><name>Y</name><value>'), N'Z', N'</value></row><row><name>Z</name><value>'), 1, 14, N''),
N'</value></row>'
)
) AS XmlData
FROM Data d
) x
I do have following table
ID Name
1 Jagan Mohan Reddy868
2 Jagan Mohan Reddy869
3 Jagan Mohan Reddy
Name column size is VARCHAR(55).
Now for some other task we need to take only 10 varchar length i.e. VARCHAR(10).
My requirement is to check that after taking the only 10 bits length of Name column value for eg if i take Name value of ID 1 i.e. Jagan Mohan Reddy868 by SUBSTRING(Name, 0,11) if it equals with another row value. here in this case the final value of SUBSTRING(Jagan Mohan Reddy868, 0,11) is equal to Name value of ID 3 row whose Name is 'Jagan Mohan Reddy'. I need to make a list of those kind rows. Can somebody help me out on how can i achieve in SQL Server.
My main check is that the truncated values of my Name column should not match with any non truncated values of Name column. If so i need to get those records.
Assuming I understand the question, I think you are looking for something like this:
Create and populate sample data (Please save us this step in your future questions)
DECLARE #T as TABLE
(
Id int identity(1,1),
Name varchar(15)
)
INSERT INTO #T VALUES
('Hi, I am Zohar.'),
('Hi, I am Peled.'),
('Hi, I am Z'),
('I''m Zohar peled')
Use a cte with a self inner join to get the list of ids that match the first 10 chars:
;WITH cte as
(
SELECT T2.Id As Id1, T1.Id As Id2
FROM #T T1
INNER JOIN #T T2 ON LEFT(T1.Name, 10) = t2.Name AND T1.Id <> T2.Id
)
Select the records from the original table, inner joined with a union of the Id1 and Id2 from the cte:
SELECT T.Id, Name
FROM #T T
INNER JOIN
(
SELECT Id1 As Id
FROM CTE
UNION
SELECT Id2
FROM CTE
) U ON T.Id = U.Id
Results:
Id Name
----------- ---------------
1 Hi, I am Zohar.
3 Hi, I am Z
Try this
SELECT Id,Name
FROM(
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name, LEFT(Name,11) ORDER BY ID) RN
FROM Tbale1 T
) Tmp
WHERE Tmp.RN = 1
loop over your column for all the values and put your substring() function inside this loop and I think in Sql index of string starts from 1 instead of 0. If you pass your string to charindex() like this
CHARINDEX('Y', 'Your String')
thus you will come to know whether it is starting from 0 or 1
and you can save your substring value as value of other column with length 10
I hope it will help you..
I think this should cover all the cases you are looking for.
-- Create Table
DECLARE #T as TABLE
(
Id int identity(1,1),
Name varchar(55)
)
-- Create Data
INSERT INTO #T VALUES
('Jagan Mohan Reddy868'),
('Jagan Mohan Reddy869'),
('Jagan Mohan Reddy'),
('Mohan Reddy'),
('Mohan Reddy123551'),
('Mohan R')
-- Get Matching Items
select *, SUBSTRING(name, 0, 11) as ShorterName
from #T
where SUBSTRING(name, 0, 11) in
(
-- get all shortnames with a count > 1
select SUBSTRING(name, 0, 11) as ShortName
from #T
group by SUBSTRING(name, 0, 11)
having COUNT(*) > 1
)
order by Name, LEN(Name)
I've got an indexed (but not unique) varchar field of Employee IDs in a table, and in a query I need to return rows that are exactly 4 numerical characters but also over 1000.
I've found various questions on here about using validation methods to check that the field contains 0-9 characters, or doesn't contain a-z characters etc, but these are unrelated to this question.
Background:
I've got a table with various values, sample set as follows:
EmployeeID
----------
6745
EMP1
EMP2
1874
LTST
5694
0014
What I would like to do is return all values except EMP1, EMP2, LTST and 0014.
My question is, are there any reasons why I shouldn't use a Where clause like where EmployeeID between '1000' and '9999'? Reason for this being employeeid is a varchar column
If I can do this, should I also Order By employee ID, or does this not matter?
I believe "0014" would be left out of the where clause between '1000' and '9999', so that's a reason. Perhaps between '0000' and '9999' would suit your purposes better. Just remember that you're still sorting based on text. If you have any entries like "1_99", this would also show up in your query results with your given between clause.
If you're looking to only return 4-character numbers excluding leading zeroes, then the following addition should suffice:
WHERE EmployeeID BETWEEN '1000' AND '9999' AND TRY_CAST(EmployeeID As int) IS NOT NULL
...or, more intuitively:
WHERE TRY_CAST(EmployeeID As int) BETWEEN 1000 AND 9999
Run the following code as an example and you'll see that SQL Server doesn't treat INT the same as integers stored as VARCHAR:
WITH IntsAsVars
AS (
SELECT var = '1000',
int = 1000
UNION ALL
SELECT var = '100',
int = 100
UNION ALL
SELECT var = '9999',
int = 999
UNION ALL
SELECT var = '99',
int = 99
UNION ALL
SELECT var = '750',
int = 750
UNION ALL
SELECT var = '10',
int = 10
UNION ALL
SELECT var = '2',
int = 2
)
SELECT *
FROM IntsAsVars
--WHERE var BETWEEN '2' AND '750'
/* should return 2, 10, 99, 100 & 750 if it works like INT
but does it? */
ORDER BY
--var ASC,
int ASC;
Running it without the where clause gets the following so SQL Server doesn't consider the other records to be between 2 and 750 when they are stored as varchar.:
If your real data is exactly as the sample data in regard of the non-numeric values beginning with a letter, you could use your query to achieve the desired result.
However be aware of of the sort order of the data. If you have got an EmployeeId of 1ABC it will be included in the data returned by WHERE EmployeeID BETWEEN '1000' AND '9999'!
Your approach is not suitable to filter out non-numeric values!
An additional ORDER BY affects the order of the results only, it has no effect on the evaluation of the WHERE condition.
I'd say the simplest way is to use like:
select * from yourtable
where EmployeeID like '[1-9][0-9][0-9][0-9]'
lets say you have this input:
IF OBJECT_ID('tempdb..#test') IS NOT NULL
DROP TABLE #test
CREATE TABLE #test
(
EmployeeID VARCHAR(255)
)
CREATE CLUSTERED INDEX CIX_test_EmployeeID ON #test(EmployeeID)
INSERT INTO #test
VALUES
('6745'),
('EMP1'),
('EMP2'),
('1874'),
('LTST'),
('5694'),
('1000'),
('9999'),
('10L'),
('187'),
('9X9'),
('7est'),
('1ok'),
('0_o'),
('0014');
Your statement would also return '1ok','187', '10L' and so on.
Since you mentioned that your employeeID has a fixed length, you could use something like this:
SELECT *
FROM #test
WHERE EmployeeID LIKE '[1-9][0-9][0-9][0-9]'
What I'm looking for is a way in MSSQL to create a complex IN or LIKE clause that contains a SET of values, some of which will be ranges.
Sort of like this, there are some single numbers, but also some ranges of numbers.
EX: SELECT * FROM table WHERE field LIKE/IN '1-10, 13, 24, 51-60'
I need to find a way to do this WITHOUT having to specify every number in the ranges separately AND without having to say "field LIKE blah OR field BETWEEN blah AND blah OR field LIKE blah.
This is just a simple example but the real query will have many groups and large ranges in it so all the OR's will not work.
One fairly easy way to do this would be to load a temp table with your values/ranges:
CREATE TABLE #Ranges (ValA int, ValB int)
INSERT INTO #Ranges
VALUES
(1, 10)
,(13, NULL)
,(24, NULL)
,(51,60)
SELECT *
FROM Table t
JOIN #Ranges R
ON (t.Field = R.ValA AND R.ValB IS NULL)
OR (t.Field BETWEEN R.ValA and R.ValB AND R.ValB IS NOT NULL)
The BETWEEN won't scale that well, though, so you may want to consider expanding this to include all values and eliminating ranges.
You can do this with CTEs.
First, create a numbers/tally table if you don't already have one (it might be better to make it permanent instead of temporary if you are going to use it a lot):
;WITH Numbers AS
(
SELECT
1 as Value
UNION ALL
SELECT
Numbers.Value + 1
FROM
Numbers
)
SELECT TOP 1000
Value
INTO ##Numbers
FROM
Numbers
OPTION (MAXRECURSION 1000)
Then you can use a CTE to parse the comma delimited string and join the ranges with the numbers table to get the "NewValue" column which contains the whole list of numbers you are looking for:
DECLARE #TestData varchar(50) = '1-10,13,24,51-60'
;WITH CTE AS
(
SELECT
1 AS RowCounter,
1 AS StartPosition,
CHARINDEX(',',#TestData) AS EndPosition
UNION ALL
SELECT
CTE.RowCounter + 1,
EndPosition + 1,
CHARINDEX(',',#TestData, CTE.EndPosition+1)
FROM CTE
WHERE
CTE.EndPosition > 0
)
SELECT
u.Value,
u.StartValue,
u.EndValue,
n.Value as NewValue
FROM
(
SELECT
Value,
SUBSTRING(Value,1,CASE WHEN CHARINDEX('-',Value) > 0 THEN CHARINDEX('-',Value)-1 ELSE LEN(Value) END) AS StartValue,
SUBSTRING(Value,CASE WHEN CHARINDEX('-',Value) > 0 THEN CHARINDEX('-',Value)+1 ELSE 1 END,LEN(Value)- CHARINDEX('-',Value)) AS EndValue
FROM
(
SELECT
SUBSTRING(#TestData, StartPosition, CASE WHEN EndPosition > 0 THEN EndPosition-StartPosition ELSE LEN(#TestData)-StartPosition+1 END) AS Value
FROM
CTE
)t
)u INNER JOIN ##Numbers n ON n.Value BETWEEN u.StartValue AND u.EndValue
All you would need to do once you have that is query the results using an IN statement, so something like
SELECT * FROM MyTable WHERE Value IN (SELECT NewValue FROM (/*subquery from above*/)t)