Split column value to match yes or no - sql

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

Related

Looping through a column to check if a cast succeeds in SQL Server

I am trying to check if an entire column which is varchar and make sure it can be cast to float. I have a cursor portion like so:
DECLARE #CastFailed BIT
SET #CastFailed = (SELECT SUM(CASE WHEN TRY_CAST(#ColumnName AS FLOAT) IS NULL THEN 1
ELSE 0 END) AS CastResult)
-- Look at this
PRINT #CastFailed
IF #CastFailed > 0
BEGIN
PRINT 'ERROR: ' + #ColumnName + ' cannot be converted to FLOAT type'
SET #HasErrors = 1
END
ELSE
BEGIN
PRINT 'The cast has passed.'
END
For some reason, it is always returning 1. I already in a previous part of the cursor (not shown but above), verified that the column passed in (#ColumnName) is NOT NULL at any point.
I need to find out if all the CAST to FLOAT for #ColumnName are valid. The cursor loops through a table of columns bring in the FETCH #ColumnName one by one. What am I missing?
Easy:
DECLARE #t TABLE (txt VARCHAR(100));
INSERT #t VALUES ('ABC123'),('100.00'),('100'),('11.222.333'),('00');
DECLARE #CastFailed BIT =
(SELECT ISNULL(MAX(1),0) FROM #t AS t WHERE TRY_CAST(t.Txt AS FLOAT) IS NULL);
SELECT CastFailed = #CastFailed;
For even better performance ...
DECLARE #t TABLE (txt VARCHAR(100));
INSERT #t VALUES ('ABC123'),('100.00'),('100'),('11.222.333'),('00');
DECLARE #CastFailed BIT =
(ISNULL((SELECT TOP(1) 1 FROM
(SELECT 1 FROM #t AS t WHERE TRY_CAST(t.Txt AS FLOAT) IS NULL) AS x(x)),0));
SELECT CastFailed = #CastFailed;
Here is an option where you can avoid cursors and Dynamic SQL. It will dynamically UNPIVOT your data and return the columns which fail the conversion to float
(2008 & 2012 Compatible)
Example
Declare #YourTable Table (id int,[Col1] varchar(50),[Col2] varchar(50))
Insert Into #YourTable Values
(1,'1e6','ABC') -- This Col2 will fail Conversion
,(2,'5.5','25')
,(3,'50.25','0')
Select C.Col
,Failed = count(*)
from #YourTable A
Cross Apply ( values ( convert(xml,(Select A.* for XML RAW)) ) )B(XMLData)
Cross Apply (
Select Col = xAttr.value('local-name(.)', 'varchar(100)')
,Value = xAttr.value('.','varchar(max)')
From XMLData.nodes('//#*') xNode(xAttr)
) C
Where Col in ('Col1','Col2') -- Or you can Exclude Columns ... Where Col NOT in ('id','OtherCols','ToExclude')
and try_convert(float,value) is null
Group BY C.Col
Results
Col Failed
Col2 1

Get row that had multi string in name SQL server

I'm using SQL server 2014 I try to get row that have multi-value in name like:
declare #value ='m n'
So the value that return should have 'm' and 'n' in any position
I tried to use
Select * from Table where contains(name,N'"*m*" and "*n*"')
But the value return only if it started by 'm' and 'n'
I had also tried:
select * from Table where name like '%m%n%'
the value return only if name contains 'm' then 'n' not 'n' then 'm'.
Note: I'm getting value from parameter so I don't known how many char or what position so I'm using replace on #value to get what I want.
Since you want to use that variable.
Then best split the letters or words.
Having the STRING_SPLIT function would be great for this.
But there are more ways to split strings.
The example below does it with a WHILE loop.
CREATE TABLE [Table]
( id INT IDENTITY(101,1) PRIMARY KEY,
name NVARCHAR(100) NOT NULL
);
GO
✓
INSERT INTO [Table] (name) VALUES
('a munchkin'),
('never ever sever mah lever'),
('saintess'),
('them mammaries');
GO
4 rows affected
DECLARE #value NVARCHAR(1000);
SET #value =' m n ';
DECLARE #values TABLE (
value NVARCHAR(42)
);
DECLARE #words NVARCHAR(1000);
DECLARE #word NVARCHAR(42);
SET #words = RTRIM(LTRIM(#value))+' ';
WHILE CHARINDEX(' ', #words) > 0
BEGIN
SET #word = SUBSTRING(#words,0,CHARINDEX(' ',#words))
SET #words = LTRIM(SUBSTRING(#words,CHARINDEX(' ',#words)+1,LEN(#words)))
IF #word != '' INSERT INTO #values (value) VALUES (#word);
END;
DECLARE #TotalValues INT;
SET #TotalValues = (select count(distinct value) from #values);
--
-- use the table variable to query the table
--
SELECT *
FROM [Table] t
WHERE EXISTS
(
SELECT 1
FROM #values v
WHERE t.name LIKE '%'+v.value+'%'
HAVING COUNT(DISTINCT v.value) = #TotalValues
);
GO
id | name
--: | :-------------------------
101 | a munchkin
102 | never ever sever mah lever
db<>fiddle here
You have to separate your conditions like this
select * from Table where name like '%m%' and name like '%n%'

Show other column value if the value of the column is NULL or blank

I want to display value of other column if the value of my column is NULL or blank. Below is my table.
DECLARE #Tab TABLE(ID INT, suser VARCHAR(10), sgroup VARCHAR(10), sregion VARCHAR(10))
INSERT INTO #Tab VALUES(1,'Test',NULL,NULL),(2,'','Group',NULL),(3,NULL,NULL,'Region'),(4,NULL,NULL,NULL)
SELECT * from #Tab
My Query:
SELECT ID
,Case WHEN suser IS NULL OR suser = ''
THEN sgroup
WHEN sgroup IS NULL OR sgroup = ''
THEN sregion
ELSE NULL
END AS col
from #Tab
I want oupput as:-
DECLARE #Tab1 TABLE(ID INT, col VARCHAR(10))
INSERT INTO #Tab1 VALUES(1,'Test'),(2,'Group'),(3,'Region'),(4,NULL)
SELECT * from #Tab1
Thanks
Blank and NULL are not the same. If you want to treat '' and NULL as the same value, one method would be to use NULLIF:
ISNULL(NULLIF(YourFirstColumn,''),YourOtherColumn)
Ideally, however, if either could be stored in your data but they should be treated as the same, don't allow one of them. Personally, I would update all the values of the column to NULL where they have a value of '' and then add a constraint that doesn't allow the value ''. Something like:
UPDATE YourTable
SET YourColumn = NULL
WHERE YourColumn = '';
ALTER TABLE YourTable ADD CONSTRAINT YourColumn_NotBlank CHECK (YourColumn IS NULL OR YourColumn <> '');
use COALESCE function it will return 1st non null value
SELECT ID ,COALESCE(suser , sgroup, sregion)
col
from #Tab

Delimiter with a condition

I have a column (MarketID) in a table.
I have to derive a value out of it.
I have to check for occurrence of delimiter(.) in the second position and see if there are consecutive three numbers after the delimiter then get that value. If not check for occurrence of delimiter(.) in the fourth position and see if there are consecutive three numbers after the delimiter then get that value
else get 0.
1) In first record: '3.001.1.16', at the second position there is a delimiter(.) and consecutive 3 number exists (001), so my output would be 001..
2)In the second record '3.1.006.4.7',there is a delimiter at second position but we don't have three consecutive numbers so we check for the 4th position and there is a delimiter and consecutive three numbers exist so the output is 006 ..
3) no (.) delimiter so output=0.
create table dbo.SampleList
(
MarketID varchar(100)
)
insert into dbo.SampleList
select '3.001.1.16'
union all
select '3.1.006.4.7'
union all
select 'D16B000000:21109:4'
select * from dbo.SampleList
Assuming SQL Server from dbo, you could use a CASE statement:
SELECT MarketID,
CASE WHEN SUBSTRING(MarketID,2,1) = '.' AND TRY_CONVERT(int,SUBSTRING(MarketID,3,3)) IS NOT NULL THEN SUBSTRING(MarketID,3,3)
WHEN SUBSTRING(MarketID,4,1) = '.' AND TRY_CONVERT(int,SUBSTRING(MarketID,5,3)) IS NOT NULL THEN SUBSTRING(MarketID,5,3)
ELSE '0'
END
FROM #SampleList
TRY_CONVERT to int will verify that the 3 characters are numbers
Here's a solution using a function I've created a few years ago.
It allows you to split a string and get a table as a result.
CREATE FUNCTION [dbo].[splitStringToTable]
(
#List VARCHAR(MAX) ,
#Separator VARCHAR(MAX)
)
RETURNS #Results TABLE
(
ID INT
)
AS
BEGIN
SET #List = #List + ','
DECLARE #POS INT
DECLARE #TEMP VARCHAR(8000)
WHILE (Charindex(#Separator, #List)>0)
BEGIN
SET #POS = Charindex(#Separator, #List)
IF #POS > = 0
BEGIN
SET #TEMP = LEFT(#List, #POS-1)
IF #TEMP <> ''
INSERT INTO #Results (ID) VALUES (#TEMP)
SET #List = Substring(#List, Charindex(#Separator, #List)+len(#Separator), len(#List))
END
END
RETURN
END
GO
Usage:
SELECT *, ISNULL((SELECT TOP 1 ID FROM dbo.[splitStringToStringTable](MarketID, '.') WHERE LEN(ID) = 3), 0) AS Result
FROM SampleList
SELECT MarketID,
(CASE WHEN SUBSTRING(MarketID,2,1) = '.'
THEN
(CASE WHEN SUBSTRING(MarketID,6,1) = '.' THEN SUBSTRING (MarketID,3,3)
WHEN SUBSTRING(MarketID,4,1) = '.' THEN
(CASE WHEN SUBSTRING(MarketID ,8,1)='.' THEN SUBSTRING(MarketID,5,3) ELSE NULL END)ELSE NULL END)
WHEN MarketID NOT LIKE '%.%' THEN '0'
ELSE '0'
END ) AS Output
FROM dbo.SampleList

SQL dynamic columns and Update multiple columns

I have a table UserPermission which has a number of columns of TINYINT type. e.g Read, Write, Update, Delete, Access etc.
I get three parameters in the stored procedure: #UserId, #ColNames, #ColValues where #ColNames and #ColValues are comma separated values.
How can I insert or update the table row (if already exists) with the passed column names and corresponding values.
I try to write the dynamic query which runs fine for INSERT but I was unable to write the UPDATE query dynamically with each column and its value to be concatenate.
Any response would be appreciated
Thanks in advance.
This is a somewhat dirty way to do what you require. However, if you create the following Stored Procedure:
CREATE FUNCTION [dbo].[stringSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
You can then use that Procedure to join the data together:
DECLARE #TotalCols INT
DECLARE #TotalVals INT
SET #TotalCols = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('department, teamlead', ',')
);
SET #TotalVals = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('IT, Bob', ',')
);
IF #TotalCols = #TotalVals
BEGIN
IF OBJECT_ID('tempdb..#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
ColName VARCHAR(MAX) NULL
,ColValue VARCHAR(MAX) NULL
)
INSERT INTO #temptable
SELECT a.DATA
,b.DATA
FROM dbo.stringSplit('department, teamlead', ',') AS a
INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id
SELECT *
FROM #temptable;
END
It's not very efficient, but it will bring you the desired results.
You can then use the temp table to update, insert and delete as required.
Instead of having a comma delimited list I would create a separate parameter for each Column and make its default value to NULL and in the code update nothing if its null or insert 0. Something like this....
CREATE PROCEDURE usp_UserPermissions
#UserID INT
,#Update INT = NULL --<-- Make default values NULL
,#Delete INT = NULL
,#Read INT = NULL
,#Write INT = NULL
,#Access INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #t TABLE (UserID INT, [Update] INT,[Read] INT
,[Write] INT,[Delete] INT,[Access] INT)
INSERT INTO #t (Userid, [Update],[Read],[Write],[Delete],[Access])
VALUES (#UserID , #Update , #Read, #Write , #Delete, #Access)
IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = #UserID)
BEGIN
UPDATE up -- Only update if a value was provided else update to itself
SET up.[Read] = ISNULL(t.[Read] , up.[Read])
,up.[Write] = ISNULL(t.[Write] , up.[Write])
,up.[Update] = ISNULL(t.[Update] , up.[Update])
,up.[Delete] = ISNULL(t.[Delete] , up.[Delete])
,up.[Access] = ISNULL(t.[Access] , up.[Access])
FROM UserPermission up
INNER JOIN #t t ON up.UserID = t.UserID
END
ELSE
BEGIN
-- if already no row exists for that User add a row
-- If no value was passed for a column add 0 as default
INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access])
SELECT Userid
, ISNULL([Update], 0)
, ISNULL([Read], 0)
, ISNULL([Write], 0)
, ISNULL([Delete], 0)
, ISNULL([Access], 0)
FROM #t
END
END