ensure same UPCs in every assortment-group with SQL - sql

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

Related

Extract multiple strings from a column

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

Want to compare 4 different columns with the result of CTE

I have created a CTE (common table Expression) as follows:
DECLARE #N VARCHAR(100)
WITH CAT_NAM AS (
SELECT ID, NAME
FROM TABLE1
WHERE YEAR(DATE) = YEAR(GETDATE())
)
SELECT #N = STUFF((
SELECT ','''+ NAME+''''
FROM CAT_NAM
WHERE ID IN (20,23,25,30,37)
FOR XML PATH ('')
),1,1,'')
The result of above CTE is 'A','B','C','D','F'
Now I need to check 4 different columns CAT_NAM_1,CAT_NAM_2,CAT_NAM_3,CAT_NAM_4 in the result of CTE and form it as one column like follow:
Select
case when CAT_NAM_1 in (#N) then CAT_NAM_1
when CAT_NAM_2 in (#N) then CAT_NAM_2
when CAT_NAM_3 in (#N) then CAT_NAM_3
when CAT_NAM_4 in (#N) then CAT_NAM_4
end as CAT
from table2
When I'm trying to do the above getting error please help me to do.
If my approach is wrong help me with right one.
I am not exactly sure what you are trying to do, but if I understand the following script shows one possible technique. I have created some table variables to mimic the data you presented and then wrote a SELECT statement to do what I think you asked (but I am not sure).
DECLARE #TABLE1 AS TABLE (
ID INT NOT NULL,
[NAME] VARCHAR(10) NOT NULL,
[DATE] DATE NOT NULL
);
INSERT INTO #TABLE1(ID,[NAME],[DATE])
VALUES (20, 'A', '2021-01-01'), (23, 'B', '2021-02-01'),
(25, 'C', '2021-03-01'),(30, 'D', '2021-04-01'),
(37, 'E', '2021-05-01'),(40, 'F', '2021-06-01');
DECLARE #TABLE2 AS TABLE (
ID INT NOT NULL,
CAT_NAM_1 VARCHAR(10) NULL,
CAT_NAM_2 VARCHAR(10) NULL,
CAT_NAM_3 VARCHAR(10) NULL,
CAT_NAM_4 VARCHAR(10) NULL
);
INSERT INTO #TABLE2(ID,CAT_NAM_1,CAT_NAM_2,CAT_NAM_3,CAT_NAM_4)
VALUES (1,'A',NULL,NULL,NULL),(2,NULL,'B',NULL,NULL);
;WITH CAT_NAM AS (
SELECT ID, [NAME]
FROM #TABLE1
WHERE YEAR([DATE]) = YEAR(GETDATE())
AND ID IN (20,23,25,30,37,40)
)
SELECT CASE
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_1) THEN CAT_NAM_1
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_2) THEN CAT_NAM_2
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_3) THEN CAT_NAM_3
WHEN EXISTS(SELECT 1 FROM CAT_NAM WHERE CAT_NAM.[NAME] = CAT_NAM_4) THEN CAT_NAM_4
ELSE '?' -- not sure what you want if there is no match
END AS CAT
FROM #TABLE2;
You can do a bit of set-based logic for this
SELECT
ct.NAME
FROM table2 t2
CROSS APPLY (
SELECT v.NAME
FROM (VALUES
(t2.CAT_NAM_1),
(t2.CAT_NAM_2),
(t2.CAT_NAM_3),
(t2.CAT_NAM_4)
) v(NAME)
INTERSECT
SELECT ct.NAM
FROM CAT_NAM ct
WHERE ct.ID IN (20,23,25,30,37)
) ct;

How to compare String Variable with Integer

I have this table structure and and some sample data. I want return data for multiple ids via parameter. I have declared a parameter string and now I want to compare it with the column but it ain't allowing because ID is integer.
Can anybody give me any help here ?
CREATE TABLE EMPLOYEE
(
ID INT,
EMPLOYEE_NAME VARCHAR(50)
);
INSERT INTO EMPLOYEE VALUES (1, 'Isaac Frempong');
INSERT INTO EMPLOYEE VALUES (2, 'Eric Ortizz');
DECLARE #StrID VARCHAR(20) = '1, 2'
SELECT * FROM EMPLOYEE
WHERE ID = #StrID
SELECT * FROM EMPLOYEE
WHERE #StrID+',' LIKE '%'+cast(ID as varchar(20))+'%,'
Pretty bad performance as it will need to do a table scan but safe enough.
Generally though, your list of IDs should be a table variable you can do a proper JOIN or IN with
The easiest solution is to use dynamic SQL
DECLARE #sql VARCHAR(1000) = 'SELECT * FROM EMPLOYEE WHERE ID IN (' + #StrID + ')';
EXEC(#sql);
For SQL Server 2017+ you could use STRING_SPLIT a table-valued function that splits a string into rows of substrings
CREATE TABLE EMPLOYEE
(
ID INT,
EMPLOYEE_NAME VARCHAR(50)
);
INSERT INTO EMPLOYEE VALUES (1, 'Isaac Frempong');
INSERT INTO EMPLOYEE VALUES (2, 'Eric Ortizz');
DECLARE #StrID VARCHAR(20) = '1, 2'
SELECT * FROM EMPLOYEE
WHERE ID IN (SELECT value FROM STRING_SPLIT (#StrID,','))
Refer this working fiddle
Create a user defined table type and pass it as a parameter.
CREATE TYPE [UDT_INTIDS] AS TABLE(
[ID] [int] NOT NULL
)
GO
-- create a table value
DECLARE #IDs [UDT_INTIDS];
INSERT #IDs VALUES (1),(2);
-- search using table value.
SELECT e.*
FROM EMPLOYEE e
WHERE e.ID IN (SELECT p.ID FROM #IDs p);
-- or
SELECT e.*
FROM EMPLOYEE e
JOIN #IDs p ON e.ID = p.ID;
See https://learn.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine?view=sql-server-2017 for more details.
You can use the Cast in SQL-Server to cast it to the appropriate datatype. Source Here
WHERE CAST(ID AS VARCHAR(20)) = #StrID
Alternatively: You can use CONVERT function.
WHERE CONVERT(VARCHAR(20), ID) = #StrID

How to join two dynamic sql result set and get output where column name is not predefined in SQL Server stored procedure

DECLARE #sql1 NVARCHAR(MAX),
#sql2 NVARCHAR(MAX);
Let's create a temp table named #Table1 to put output from first dynamic query
CREATE TABLE #Table1
(
[CustomerID] varchar(20) NULL,
[CustomerName] varchar(20) NULL,
[DColumn01] char(1) NULL,
[DColumn02] char(1) NULL,
[DColumn03] char(1) NULL,
[DColumn04] char(1) NULL
-- .....
-- .....
[DColumn10] char(1) NULL
);
Let's create a temp table named #Table2 to put output from second dynamic query
CREATE TABLE #Table2
(
[CustomerID] varchar(20) NULL,
[CustomerName] varchar(20) NULL,
[PColumn01] char(1) NULL,
[PColumn02] char(1) NULL,
[PColumn03] char(1) NULL,
[PColumn04] char(1) NULL
-- .....
-- .....
[PColumn10] char(1) NULL
-- .....
-- .....
[PColumn20] char(1) NULL
);
Based on some condition first dynamic query select output look like below select query from #Table1:
SET #sql1 = N'SELECT [CustomerID], [CustomerName], [DColumn01], [DColumn02], [DColumn03], [DColumn04], [DColumn10] FROM #Table1';
EXECUTE sp_executesql #sql1;
Here select column can vary based on other dynamic condition. Here column name can vary from "01" to "10" for DColumn like [DColumn09]
Based on some condition second dynamic query select output look like below select query from #Table2
SET #sql2 = N'SELECT [CustomerID], [CustomerName], [PColumn01], [PColumn02], [PColumn03], [PColumn04], [PColumn10], [PColumn20] FROM #Table2';
EXECUTE sp_executesql #sql2;
Here select column can vary based on other dynamic condition. Here column name can vary from "01" to "20" for PColumn like [PColumn15].
Now I need to join both dynamic query based on [CustomerID].
[CustomerID], [CustomerName], [DColumn01], [DColumn02], [DColumn03], [DColumn04], ....., [DColumn10], [PColumn01], [PColumn02], [PColumn03], [PColumn04], ....., [PColumn10], ....., [PColumn20]
AS the select query can vary based on column name, so I am not able to insert into any temp table.
My thought:
SET #sql1 = N'SELECT [CustomerID], [CustomerName], [DColumn01], [DColumn02], [DColumn03], [DColumn04], [DColumn10]
INTO #abc
FROM #Table1;
SELECT [CustomerID] AS [PCustomerID], [CustomerName] AS [PCustomerName], [PColumn01], [PColumn02], [PColumn03], [PColumn04], [PColumn10], [PColumn20]
INTO #xyz
FROM #Table2;
SELECT t1.*, t2.*
FROM #Table1 AS t1
INNER JOIN #Table2 AS t2 ON t1.[CustomerID] = t2.[CustomerID]';
EXECUTE sp_executesql #sql1;
DROP TABLE #Table1;
DROP TABLE #Table2;
How can i join that two dynamic SQL result set and get output where column name is not predefined? I tried to do one way, but not sure is there any better way to do that

Assign multiple values to Table variable in SQL

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