Group Numbers based on Level - sql

I have requirement in SQL Server database where I have to group the data. please find the image attached for reference.
If the Level is more than 2 then it has to be grouped under level 2 (ex: 1.1.1, 1.1.2 are rolled up under 1.1.) and if there isn't any level 2 available then have to create a second level based on the level 3 Numbers (ex: 1.2.1)
Thanks

If it's not certain that the first 2 numbers are single digits:
declare #T table ([Column] varchar(20));
insert into #T values ('1'),('1.2.'),('1.2.3.'),('1.2.3.4.'),('10.20.'),('10.20.30.'),('10.20.30.40.');
select
case when [Column] like '%.%.%' then substring([Column],1,charindex('.',[Column],charindex('.',[Column])+1)) else [Column] end as [Derived Col1],
case when [Column] like '%.%.%.%' then [Column] else '' end as [Derived Col2]
from #T;
If it's certain that the first 2 numbers are single digits then it can be simplified:
declare #T table ([Column] varchar(20));
insert into #T values ('1'),('1.2.'),('1.2.3.'),('1.2.3.4.');
select
substring([Column],1,4) as [Derived Col1],
case when [Column] like '%.%.%.%' then [Column] else '' end as [Derived Col2]
from #T;

you can use CHARINDEX to locate '.' and then decide DERIVED_COL_2.
SELECT COLUMN,
SUBSTRING(COLUMN,1,4)DERIVED_COL_1,
CASE WHEN CHARINDEX('.',COLUMN,5) = 6 THEN COLUMN END AS DERIVED_COL_2
FROM YOUR_TABLE

Would this do, if you need the new values as computed columns?
Updated: this works with double digits and the third column is null for 1st/2nd level version
CREATE TABLE Test
(
Version varchar(30),
VersionMain AS CASE
WHEN CHARINDEX('.',Version,4) > 0 THEN LEFT(Version,CHARINDEX('.',Version,4)) ELSE Version END,
VersionSub AS CASE
WHEN LEN(Version) - LEN(REPLACE(Version,'.','')) >= 3 THEN Version ELSE NULL END
)

Related

SQL-using if exist in matching two columns in table

im trying to use keywords like detergent, soap, dish etc to match two column in my sql table, if the keywords find match in two column, i want to have another column saying its a matched. i am planning to use the if exist but i do not know the proper syntax.
sample column:
Column1 Column2
-----------------------------------------------
detergent powder all powder detergent
dish washing liquid dish liquid for washing
hand soap hand liquid soap
Here is the simplest solution to your question. The trick is in the "virtual" column, aliased as Match, that we create in the select statement. This column is computed using a case statement to see if the search term appears in both of the columns. Note we need to use the like statement with wildcard operators %.
create table Example (Column1 varchar(max), Column2 varchar(max));
insert into Example select 'detergent powder', 'all powder detergent';
insert into Example select 'dish washing liquid', 'dish liquid for washing' ;
insert into Example select 'hand soap', 'hand liquid soap';
declare #search varchar(20) = 'detergent';
select Column1,
Column2,
case when Column1 like '%' + #search + '%' and
Column2 like '%' + #search + '%'
then 'matched'
else 'not matched' end as [Match]
from Example;
We could also create the Match column as a "real" column in the table and modify this script slightly to update that column based on the same criteria.
Here's an example that checks if any of the 3 words appears in both columns.
Sample data:
CREATE TABLE Test (
Id INT IDENTITY(1,1) PRIMARY KEY,
Col1 VARCHAR(100),
Col2 VARCHAR(100)
);
INSERT INTO Test (Col1, Col2) VALUES
('detergent powder', 'all powder detergent'),
('dish washing liquid', 'dish liquid for washing'),
('hand soap', 'hand liquid soap'),
('soap dish', 'detergent');
Query:
SELECT t.*
, cast(
case
when exists (
select 1
from (values ('soap'),('detergent'),('dish')) s(search)
join (values (Col1),(Col2)) c(col)
on c.col like '%'+s.search+'%'
group by s.search
having count(*) = 2
) then 1 else 0 end as bit) as hasMatch
FROM Test t;
An EXISTS checks if there's at least 1 result from a query.
And the HAVING clause makes sure that 2 matches per search words are needed.
But it can also be done without that GROUP BY & HAVING clause:
SELECT t.*
, cast(case when exists (
select 1
from (values ('soap'),('detergent'),('dish')) s(search)
where Col1 like '%'+s.search+'%'
and Col2 like '%'+s.search+'%'
) then 1 else 0 end as bit) as hasMatch
FROM Test t;
A test on rextester here

how to check and change custom string in sql server

i have problem in my sql query code
i have one column for my codes and structure of code like this
3digit-1to3digit-5to7digit-1to2digit
xxx-xxx-xxxxxx-xx
in code column user add code like
1-1486414-305-115 --mistake
116-500-325663-1 --ok
116-2-2244880-1 --ok
121-512-2623075-1 --ok
122-500-1944261-3 --ok
2-2651274-500-147 --mistake
1-2551671-305-147 --mistake
124-500-329130-1 --ok
how to check and fix the mistake codes.
thanks for read my problem
Alternatively, instead of a load of LIKE expressions, you could split the parts and inspect their lengths, and follow up by checking the string only contains digits and hyphens with a LIKE. As your string specifically has 4 parts, I've used PARSENAME here, rather than a "splitter" function.
SELECT *
FROM (VALUES ('1-1486414-305-115'),
('116-500-325663-1'),
('116-2-2244880-1'),
('121-512-2623075-1'),
('122-500-1944261-3'),
('2-2651274-500-147'),
('1-2551671-305-147'),
('116-ba-2244880-1'),
('124-500-329130-1'))V(Code)
CROSS APPLY (VALUES(PARSENAME(REPLACE(V.code,'-','.'),4),
PARSENAME(REPLACE(V.code,'-','.'),3),
PARSENAME(REPLACE(V.code,'-','.'),2),
PARSENAME(REPLACE(V.code,'-','.'),1))) PN(P1, P2, P3, P4)
WHERE LEN(P1) != 3
OR NOT(LEN(P2) BETWEEN 1 AND 3)
OR NOT(LEN(P3) BETWEEN 5 AND 7)
OR NOT(LEN(P4) BETWEEN 1 AND 2)
OR V.Code LIKE '%[^0-9\-]%' ESCAPE '\';
What a pain, because SQL Server does not support regular expressions.
One method is 6 like comparisons:
where col like '[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9]' or
col like '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]'
Otherwise, you could count the -s, check the positions, and characters. So:
where col not like '[^-0-9]' and -- only has digits and -
col not like '%-%-%-%-%' and -- does not have 4 hyphens
col like '___-___-%-%[0-9]' and -- first two hyphens in the right place and ends in digit
'-' in (substring(col, 14, 1), substring(col, 15, 1), substring(col, 16, 1)) -- last hyphen in the right place
Here is the complete code which can achieve the required result
1) store the splitted string into a table #myvalues (I have written a solution to split a string into many rows using Recusrsivity in this Link )
2) Store the conditions in Table #tabcheck (length of each string)
3) Make a jointure between #myvalues and #tabcheck to get the result
declare #str as nvarchar(max)
set #str='116-500-325663-1';
declare #separator as char(1)
set #separator='-';
declare #tabcheck as table(id int,fromval int ,toval int)
insert into #tabcheck values(1,3,3),(2,1,3),(3,5,7),(4,1,2);
declare #myvalues as table(id int identity(1,1),myval varchar(100));
with cte as(
select #str [mystr],
cast(1 as int) [Start],
charindex(#separator,#str)as Nd
union all
select substring(#str,nd+1,len(#str)),cast(Nd+1 as int),charindex(#separator,#str,Nd+1) from cte
where nd>0
)
insert into #myvalues(myval)
select case when nd>0 then substring(#str,start,Nd-start)
else substring(#str,start,len(#str)) end [splitted]
from cte OPTION (MAXRECURSION 1000);
declare #result as int;
with mytab as(
select t1.id,t1.myval,len(t1.myval) L,t2.fromval,t2.toval,
case when len(t1.myval)>=t2.fromval and len(t1.myval)<=t2.toval then 1 else 0 end [result]
from #myvalues t1 inner join #tabcheck t2 on t1.id=t2.id)
select #result=count(1) from mytab where result=0 ;
select case #result when 0 then 'OK' else 'Mistake' end [result]

SQL Server : multiple inserts into temp table while creating a value for each different insert

Pardon my code, I am still learning.
What I want to do is take several select statements and insert them into a single temp table. For each select statement I would like some sort of identification to know which select statement the data came from.
I have a crude idea below to use the case statement below.
Error:
Msg 8152, Level 16, State 14, Line 14
String or binary data would be truncated.
Code:
if object_id('tempdb..#PP') is not null
drop table #PP
SELECT
#Pr.*,
Case
When TranID = TranID then 'BK Problem'
Else 'Error' End as 'Test'
INTO #PP
FROM #Pr
WHERE TypeID = 't'
--CCA Test
INSERT INTO #PP
SELECT
#Pr.*,
Case
When TranID = TranID then 'CCA Problem'
Else 'Error' End as 'Test'
FROM #Pr
WHERE TypeID = 'r'
I appreciate any help pointing me in the right direction.
Thanks
If you use SELECT.. INTO.. like this, the column length is decided by the max length of each column on the first insert.
So.. inserting 'BK Problem' first, creates the Test column with a length of 'BK Problem', or 10.
(Do this to see: PRINT LEN('BK Problem'))
Then inserting 'CCA Problem' into Test fails, because it is a length of 11 and is too long.
I don't understand the need for your case statement either, try this:
SELECT #Pr.*, CAST('BK Problem' as VARCHAR(11)) Test
INTO #PP
FROM #Pr
WHERE TypeID = 't'
--CCA Test
INSERT INTO #PP
SELECT #Pr.*, 'CCA Problem'
FROM #Pr
WHERE TypeID = 'r'
The issue is your case expression. When you create the table using select into it creates the "Test" column as varchar(10). But in your insert statement the value is 11 characters long. You can avoid this by cast/convert on your original case expression to make the column wide enough.
SELECT #Pr.*,
convert(varchar(20), Case
When TranID = TranID then 'BK Problem'
Else 'Error' End) as 'Test'
INTO #PP
FROM #Pr
WHERE TypeID = 't'

Split column value to match yes or no

I have two tables named Retail and Activity and the data is as shown below:
Retail Table
Activity Table
My main concern is about Ok and Fault column of the table Retail, as you can see it contains comma separated value of ActivityId.
What i want is, if the Ok column has ActivityId the corresponding column will have Yes, if the Fault column has ActivityId then it should be marked as No
Note I have only four columns that is fixed, it means i have to check that either four of the columns has its value in Ok or Fault, if yes then only i have to print yes or no, otherwise null.
Desired result should be like :
If the value is in Ok then yes other wise No.
I guessing you want to store 'yes' or 'No' in some column. Below is the query to update that column :
UPDATE RetailTable
SET <Result_Column>=
CASE
WHEN Ok IS NOT NULL THEN 'Yes'
WHEN Fault IS NOT NULL THEN 'No'
END
You can use below code as staring point:
DECLARE #Retail TABLE
(
PhoneAuditID INT,
HandsetQuoteID INT,
Ok VARCHAR(50)
)
INSERT INTO #Retail VALUES (1, 1009228, '4,22,5')
INSERT INTO #Retail VALUES (2, 1009229, '1')
DECLARE #Activity TABLE
(
ID INT,
Activity VARCHAR(50)
)
INSERT INTO #Activity VALUES (1, 'BatteryOK?'), (4, 'PhonePowersUp?'), (22,'SomeOtherQuestion?'), (5,'LCD works OK?')
SELECT R.[PhoneAuditID], R.[HandsetQuoteID], A.[Activity], [Ok] = CASE WHEN A.[ID] IS NOT NULL THEN 'Yes' END
FROM #Retail R
CROSS APPLY dbo.Split(R.Ok, ',') S
LEFT JOIN #Activity A ON S.[items] = A.[ID]
I have used Split function provided here:
separate comma separated values and store in table in sql server
Try following query. i have used pivot to show row as columns. I have also used split function to split id values which you can find easily on net:
CREATE TABLE PhoneAudit
(
PhoneAuditRetailID INT,
HandsetQuoteID INT,
Ok VARCHAR(50),
Fault VARCHAR(50)
)
INSERT INTO PhoneAudit VALUES (1,10090,'1,2','3')
CREATE TABLE ActivityT
(
ID INT,
Activity VARCHAR(100)
)
INSERT INTO ActivityT VALUES (1,'Battery')
INSERT INTO ActivityT VALUES (2,'HasCharger')
INSERT INTO ActivityT VALUES (3,'HasMemoryCard')
INSERT INTO ActivityT VALUES (4,'Test')
DECLARE #SQL AS NVARCHAR(MAX)
DECLARE #ColumnName AS NVARCHAR(MAX)
SELECT #ColumnName= ISNULL(#ColumnName + ',','') + QUOTENAME(Activity) FROM (SELECT DISTINCT Activity FROM ActivityT) AS Activities
SET #SQL = 'SELECT PhoneAuditRetailID, HandsetQuoteID,
' + #ColumnName + '
FROM
(SELECT
t1.PhoneAuditRetailID,
t1.HandsetQuoteID,
TEMPOK.*
FROM
PhoneAudit t1
CROSS APPLY
(
SELECT
Activity,
(CASE WHEN ID IN (SELECT * FROM dbo.SplitIDs(t1.Ok,'',''))
THEN ''YES''
ELSE ''NO''
END) AS VALUE
FROM
ActivityT t2
) AS TEMPOK) AS t3
PIVOT
(
MIN(VALUE)
FOR Activity IN ('+ #ColumnName + ')
) AS PivotTable;'
EXEC sp_executesql #SQL
DROP TABLE PhoneAudit
DROP TABLE ActivityT
There are several ways to do this. If you are looking for a purely declarative approach, you could use a recursive CTE. The following example of this is presented as a generic solution with test data which should be adaptable to your needs:
Declare #Delimiter As Varchar(2)
Set #Delimiter = ','
Declare #Strings As Table
(
String Varchar(50)
)
Insert Into #Strings
Values
('12,345,6,78,9'),
(Null),
(''),
('123')
;With String_Columns As
(
Select
String,
Case
When String Is Null Then ''
When CharIndex(#Delimiter,String,0) = 0 Then ''
When Len(String) = 0 Then ''
Else Left(String,CharIndex(#Delimiter,String,0)-1)
End As String_Column,
Case
When String Is Null Then ''
When CharIndex(#Delimiter,String,0) = 0 Then ''
When Len(String) = 0 Then ''
When Len(Left(String,CharIndex(#Delimiter,String,0)-1)) = 0 Then ''
Else Right(String,Len(String)-Len(Left(String,CharIndex(#Delimiter,String,0)-1))-1)
End As Remainder,
1 As String_Column_Number
From
#Strings
Union All
Select
String,
Case
When CharIndex(#Delimiter,Remainder,0) = 0 Then Remainder
Else Left(Remainder,CharIndex(#Delimiter,Remainder,0)-1)
End As Remainder,
Case
When CharIndex(#Delimiter,Remainder,0) = 0 Then ''
When Len(Left(Remainder,CharIndex(#Delimiter,Remainder,0)-1)) = 0 Then ''
Else Right(Remainder,Len(Remainder)-Len(Left(Remainder,CharIndex(#Delimiter,Remainder,0)-1))-1)
End As Remainder,
String_Column_Number + 1
From
String_Columns
Where
(Remainder Is Not Null And Len(Remainder) > 1)
)
Select
String,
String_Column,
String_Column_Number
From
String_Columns

Finding strings with duplicate letters inside

Can somebody help me with this little task? What I need is a stored procedure that can find duplicate letters (in a row) in a string from a table "a" and after that make a new table "b" with just the id of the string that has a duplicate letter.
Something like this:
Table A
ID Name
1 Matt
2 Daave
3 Toom
4 Mike
5 Eddie
And from that table I can see that Daave, Toom, Eddie have duplicate letters in a row and I would like to make a new table and list their ID's only. Something like:
Table B
ID
2
3
5
Only 2,3,5 because that is the ID of the string that has duplicate letters in their names.
I hope this is understandable and would be very grateful for any help.
In your answer with stored procedure, you have 2 mistakes, one is missing space between column name and LIKE clause, second is missing single quotes around search parameter.
I first create user-defined scalar function which return 1 if string contains duplicate letters:
EDITED
CREATE FUNCTION FindDuplicateLetters
(
#String NVARCHAR(50)
)
RETURNS BIT
AS
BEGIN
DECLARE #Result BIT = 0
DECLARE #Counter INT = 1
WHILE (#Counter <= LEN(#String) - 1)
BEGIN
IF(ASCII((SELECT SUBSTRING(#String, #Counter, 1))) = ASCII((SELECT SUBSTRING(#String, #Counter + 1, 1))))
BEGIN
SET #Result = 1
BREAK
END
SET #Counter = #Counter + 1
END
RETURN #Result
END
GO
After function was created, just call it from simple SELECT query like following:
SELECT
*
FROM
(SELECT
*,
dbo.FindDuplicateLetters(ColumnName) AS Duplicates
FROM TableName) AS a
WHERE a.Duplicates = 1
With this combination, you will get just rows that has duplicate letters.
In any version of SQL, you can do this with a brute force approach:
select *
from t
where t.name like '%aa%' or
t.name like '%bb%' or
. . .
t.name like '%zz%'
If you have a case sensitive collation, then use:
where lower(t.name) like '%aa%' or
. . .
Here's one way.
First create a table of numbers
CREATE TABLE dbo.Numbers
(
number INT PRIMARY KEY
);
INSERT INTO dbo.Numbers
SELECT number
FROM master..spt_values
WHERE type = 'P'
AND number > 0;
Then with that in place you can use
SELECT *
FROM TableA
WHERE EXISTS (SELECT *
FROM dbo.Numbers
WHERE number < LEN(Name)
AND SUBSTRING(Name, number, 1) = SUBSTRING(Name, number + 1, 1))
Though this is an old post it's worth posting a solution that will be faster than a brute force approach or one that uses a scalar udf (which generally drag down performance). Using NGrams8K this is rather simple.
--sample data
declare #table table (id int identity primary key, [name] varchar(20));
insert #table([name]) values ('Mattaa'),('Daave'),('Toom'),('Mike'),('Eddie');
-- solution #1
select id
from #table
cross apply dbo.NGrams8k([name],1)
where charindex(replicate(token,2), [name]) > 0
group by id;
-- solution #2 (SQL 2012+ solution using LAG)
select id
from
(
select id, token, prevToken = lag(token,1) over (partition by id order by position)
from #table
cross apply dbo.NGrams8k([name],1)
) prep
where token = prevToken
group by id; -- optional id you want to remove possible duplicates.
another burte force way:
select *
from t
where t.name ~ '(.)\1';