SQL Query to Filter a Table using another Table - sql

I currently have 2 SQL tables that look like this:
and...
I need to write a SELECT statement that retrieves all products from the DataTable that contain rows that match the FilterTable.
So based on my example tables above, if I were to run the query, it would return the following result:
I recently found a question that kind of attempts this:
SQL query where ALL records in a join match a condition?
but have been unsuccessful in implementing something similar
Note - I am using Microsoft SQL Server 2008

This is a little complicated, but here is one solution. Basically you need to check to see how many records from the datatable match all the records from the filtertable. This uses a subquery to do that:
SELECT *
FROM DataTable
WHERE ID IN (
SELECT DT.ID
FROM DataTable DT
JOIN FilterTable FT ON FT.Name = DT.Name
AND FT.Value = DT.VALUE
GROUP BY DT.ID
HAVING COUNT(*) = (SELECT COUNT(*) FROM FilterTable)
)
SQL Fiddle Demo

This will work:
SELECT * FROM Data WHERE ID NOT IN (
SELECT ID FROM Data JOIN Filter
on Data.Name = Filter.Name and Data.Value <> Filter.Value
)
I set up a SQL Fiddle if you want to try other things:
http://sqlfiddle.com/#!3/38b87/6
EDIT:
Better answer:
SELECT *
FROM DATA
WHERE ID NOT IN (
SELECT ID
FROM DATA
JOIN Filter ON DATA.Name = Filter.Name
AND DATA.Value <> Filter.Value
) AND ID IN
(
SELECT ID
FROM DATA
JOIN Filter ON DATA.Name = Filter.Name
)
This now fits where there is at least one filter that matches, and none that don't.

In case you can use sp_executesql (you are using procedure).
SET NOCOUNT ON
GO
CREATE TABLE Data
(
[ID] INT
,[Name] VARCHAR(12)
,[Value] VARCHAR(2)
)
CREATE TABLE Filter
(
[Name] VARCHAR(12)
,[Value] VARCHAR(2)
)
INSERT INTO Data ([ID], [Name], [Value])
VALUES (1, 'productname', 'A')
,(1, 'cost', '20')
,(1, 'active', 'Y')
,(2, 'productname', 'A')
,(2, 'cost', '20')
,(2, 'active', 'N')
,(3, 'productname', 'B')
,(3, 'cost', '20')
,(3, 'active', 'Y')
,(4, 'productname', 'A')
,(4, 'cost', '20')
,(4, 'active', 'Y')
INSERT INTO Filter ([Name], [Value])
VALUES ('productname', 'A')
,('active', 'Y')
DECLARE #SQLColumns NVARCHAR(MAX) = SUBSTRING((SELECT DISTINCT ',[' +[Name] +']' FROM Data FOR XML PATH('')),2,4000)
DECLARE #SQLFilterColumns NVARCHAR(MAX) = SUBSTRING((SELECT 'AND [' +[Name] +'] = ''' + [Value] + ''' ' FROM Filter FOR XML PATH('')),4,4000)
DECLARE #SQLStatement NVARCHAR(MAX) = N'
;WITH DataSource ([ID]) AS
(
SELECT [ID]
FROM
(
SELECT [ID]
,[Name]
,[Value]
FROM Data
) DataSource
PIVOT
(
MAX([Value]) FOR [Name] IN (' + #SQLColumns+ ')
) PVT
WHERE ' + #SQLFilterColumns + '
)
SELECT DT.[ID]
,DT.[Name]
,DT.[Value]
FROM Data DT
INNER JOIN DataSource DS
ON DT.[ID] = DS.[ID]
'
EXECUTE sp_executesql #SQLStatement
DROP TABLE Data
DROP TABLE Filter
SET NOCOUNT OFF
GO

Here is an option using a couple of PIVOTs
DECLARE #Data table ([ID] INT, [Name] VARCHAR(12), [Value] VARCHAR(2) )
DECLARE #Filter TABLE ( [Name] VARCHAR(12), [Value] VARCHAR(2) )
INSERT INTO #Data ([ID], [Name], [Value])
VALUES (1, 'productname', 'A')
,(1, 'cost', '20')
,(1, 'active', 'Y')
,(2, 'productname', 'A')
,(2, 'cost', '20')
,(2, 'active', 'N')
,(3, 'productname', 'B')
,(3, 'cost', '20')
,(3, 'active', 'Y')
,(4, 'productname', 'A')
,(4, 'cost', '20')
,(4, 'active', 'Y')
INSERT INTO #Filter ([Name], [Value])
VALUES ('productname', 'A')
,('active', 'Y');
SELECT *
FROM ( SELECT *
FROM (select [ID], [Name], [value] from #Data) as s
PIVOT
( MAX([value]) FOR [name] in ( [productname], [active])
) as pvt) B
INNER JOIN
( SELECT *
FROM (select [name], [value] from #Filter) as f
PIVOT
( MAX([value]) for [Name] IN ([productname], [active])
) AS fpvt
) F
ON F.active = b.active and f.productname = b.productname
By doing a PIVOT on the DATA table and then on the FILTER table, it allows them to be lined up for an inner join. This returns the records that match within both,

Related

SQL Server; How to incorporate unique values from STRING_AGG?

There are a handful of discussions on how to setup unique values with respect to STRING_AGG, see:
https://stackoverflow.com/a/51646573/6534818
https://stackoverflow.com/a/50589222/6534818
but I am struggling to port them over to my case as I would like to maintain my ordering of results as well as management of NULL.
Consider the following MWE -- how might I just return the distinct/unique values for the STRING_AGG operation?
CREATE TABLE #bridge (dim2Key int, groupKey int)
INSERT #bridge (dim2Key, groupKey)
VALUES (1, 1)
,(2, 1)
,(3, 1)
,(4, 2)
,(5, NULL)
CREATE TABLE #dim2 (dim2Key int, attributeOne varchar(255))
INSERT #dim2 (dim2Key, attributeOne)
VALUES (1, 'B')
,(2, 'A')
,(3, 'A')
,(4, NULL)
CREATE TABLE #dim1 (dim1Key int, attributeTwo int, attributeThree varchar(255))
INSERT #dim1 (dim1Key, attributeTwo, attributeThree)
VALUES (1, 35, 'val1')
,(2, 25, 'val2')
,(3, 45, 'val3')
,(4, 55, 'val1')
,(5, NULL, NULL)
CREATE TABLE #fact1 (dim1Key int, factvalue1 int, groupKey int)
INSERT #fact1 (dim1Key, factvalue1, groupKey)
VALUES (1, 5, 1)
,(2, 25, 1)
,(3, 55, 1)
,(4, 99, 2)
,(5, 99, NULL)
GO
SELECT
#fact1.dim1Key,
#fact1.factvalue1,
#fact1.groupKey,
#dim1.attributeTwo,
#dim1.attributeThree,
ISNULL(#dim2.attributeOne, '<missing>')
FROM #fact1
JOIN #dim1 ON #dim1.dim1key = #fact1.dim1key
CROSS APPLY (
SELECT attributeOne = STRING_AGG(ISNULL(#dim2.attributeOne, '<missing>'), ', ') WITHIN GROUP (ORDER BY #dim2.attributeOne)
FROM #bridge b
JOIN #dim2 ON #dim2.dim2key = b.dim2key
WHERE b.groupKey = #fact1.groupKey
) #dim2
Just put it in a subquery with DISTINCT
SELECT
#fact1.dim1Key,
#fact1.factvalue1,
#fact1.groupKey,
#dim1.attributeTwo,
#dim1.attributeThree,
ISNULL(#dim2.attributeOne, '<missing>')
FROM #fact1
JOIN #dim1 ON #dim1.dim1key = #fact1.dim1key
CROSS APPLY (
SELECT
attributeOne = STRING_AGG(ISNULL(d2.attributeOne, '<missing>'), ', ') WITHIN GROUP (ORDER BY d2.attributeOne)
FROM (
SELECT DISTINCT
#dim2.attributeOne
FROM #bridge b
JOIN #dim2 ON #dim2.dim2key = b.dim2key
WHERE b.groupKey = #fact1.groupKey
) d2
) #dim2

SQL LEFT JOIN to many categories

Suppose the following easy scenario, where a product row gets connected to one primary category, subcategory, and sub-subcategory.
DECLARE #PRODUCTS TABLE (ID int, DESCRIPTION varchar(50), CAT varchar(30), SUBCAT varchar(30), SUBSUBCAT varchar(30));
INSERT #PRODUCTS (ID, DESCRIPTION, CAT, SUBCAT, SUBSUBCAT) VALUES
(1, 'NIKE MILLENIUM', '1', '10', '100'),
(2, 'NIKE CORTEZ', '1', '12', '104'),
(3, 'ADIDAS PANTS', '2', '27', '238'),
(4, 'PUMA REVOLUTION 5', '3', '35', '374'),
(5, 'SALOMON SHELTER CS', '4', '15', '135'),
(6, 'NIKE EBERNON LOW', '2', '14', '157');
DECLARE #CATS TABLE (ID int, DESCR varchar(100));
INSERT #CATS (ID, DESCR) VALUES
(1, 'MEN'),
(2, 'WOMEN'),
(3, 'UNISEX'),
(4, 'KIDS'),
(5, 'TEENS'),
(6, 'BACK TO SCHOOL');
DECLARE #SUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBCATS (ID, DESCR) VALUES
(10, 'FOOTWEAR'),
(12, 'OUTERWEAR'),
(14, 'SWIMWEAR'),
(15, 'HOODIES'),
(27, 'CLOTHING'),
(35, 'SPORTS');
DECLARE #SUBSUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBSUBCATS (ID, DESCR) VALUES
(100, 'RUNNING'),
(104, 'ZIP TOPS'),
(135, 'FLEECE'),
(157, 'BIKINIS'),
(238, 'PANTS'),
(374, 'JOGGERS');
SELECT prod.ID,
prod.DESCRIPTION,
CONCAT(cat1.DESCR, ' > ', cat2.DESCR, ' > ', cat3.DESCR) AS CATEGORIES
FROM #PRODUCTS AS prod
LEFT JOIN #CATS AS cat1 ON cat1.ID = prod.CAT
LEFT JOIN #SUBCATS AS cat2 ON cat2.ID = prod.SUBCAT
LEFT JOIN #SUBSUBCATS AS cat3 ON cat3.ID = prod.SUBSUBCAT;
Now suppose that the foreign keys on #PRODUCTS table aren't just indices to their respective tables. They are comma-separated indices to more than one categories, subcategories, and sub-subcategories like here.
DECLARE #PRODUCTS TABLE (ID int, DESCRIPTION varchar(50), CAT varchar(30), SUBCAT varchar(30), SUBSUBCAT varchar(30));
INSERT #PRODUCTS (ID, DESCRIPTION, CAT, SUBCAT, SUBSUBCAT) VALUES
(1, 'NIKE MILLENIUM', '1, 2', '10, 12', '100, 135'),
(2, 'NIKE CORTEZ', '1, 5', '12, 15', '104, 374'),
(3, 'ADIDAS PANTS', '2, 6', '27, 35', '238, 374');
DECLARE #CATS TABLE (ID int, DESCR varchar(100));
INSERT #CATS (ID, DESCR) VALUES
(1, 'MEN'),
(2, 'WOMEN'),
(3, 'UNISEX'),
(4, 'KIDS'),
(5, 'TEENS'),
(6, 'BACK TO SCHOOL');
DECLARE #SUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBCATS (ID, DESCR) VALUES
(10, 'FOOTWEAR'),
(12, 'OUTERWEAR'),
(14, 'SWIMWEAR'),
(15, 'HOODIES'),
(27, 'CLOTHING'),
(35, 'SPORTS');
DECLARE #SUBSUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBSUBCATS (ID, DESCR) VALUES
(100, 'RUNNING'),
(104, 'ZIP TOPS'),
(135, 'FLEECE'),
(157, 'BIKINIS'),
(238, 'PANTS'),
(374, 'JOGGERS');
SELECT prod.ID,
prod.DESCRIPTION
--CONCAT(cat1.DESCR, ' > ', cat2.DESCR, ' > ', cat3.DESCR) AS CATEGORIES
FROM #PRODUCTS AS prod
--LEFT JOIN #CATS AS cat1 ON cat1.ID = prod.CAT
--LEFT JOIN #SUBCATS AS cat2 ON cat2.ID = prod.SUBCAT
--LEFT JOIN #SUBSUBCATS AS cat3 ON cat3.ID = prod.SUBSUBCAT;
In this case I want to achieve the following:
Be able to retrieve the respective names of the cats, subcats, sub-subcats, ie. for cats '1, 2' be able to retrieve their names (I tried LEFT JOIN #CATS AS cat1 ON cat1.ID IN prod.CAT but it doesn't work)
Create triplets of the corresponding cats, subcats, sub-subcats, ie. for
cats '1, 2'
subcats '12, 17'
sub-subcats '239, 372'
(after retrieving the appropriate names) create pipe-separated category routes like name of cat 1 > name of subcat 12 > name of sub-subcat 239 | name of cat 2 > name of subcat 17 > name of sub-subcat 372
So, for a row like (1, 'NIKE MILLENIUM', '1, 2', '10, 12', '100, 135'),
I would like to get the following result
ID
DESCRIPTION
CATEGORIES
1
NIKE MILLENIUM
MEN > FOOTWEAR > RUNNING # WOMEN > OUTERWEAR > FLEECE (I had to use # as the delimiter of the two triplets because pipe messed with the table's columns)
In case the user stupidly stores more cat IDs than subcat IDs, or sub-subcat IDs, the query should just match the ones that have a corresponding position match, ie for
cats '1, 2'
subcats '12'
sub-subcats '239, 372'
it should just create one triplet, like name of 1 > name of 12 > name of 239
STRING_SPLIT() does not promise to return the values in a specific order, so it won't work in this case as ordinal position matters.
Use OPENJSON() split the string into separate rows to ensure the values are returned in the same order.
OPENJSON() also returns a key field, so you can join on the row number within each grouping. You'll want an INNER JOIN since your requirement is that all values in that "column" must exist.
Use STUFF() to assemble the various cat>subcat>subsubcat values.
DECLARE #PRODUCTS TABLE (ID int, DESCRIPTION varchar(50), CAT varchar(30), SUBCAT varchar(30), SUBSUBCAT varchar(30));
INSERT #PRODUCTS (ID, DESCRIPTION, CAT, SUBCAT, SUBSUBCAT) VALUES
(1, 'NIKE MILLENIUM', '1, 2', '10, 12', '100, 135'),
(2, 'NIKE CORTEZ', '1, 5', '12, 15', '104, 374'),
(3, 'ADIDAS PANTS', '2, 6, 1', '27, 35, 10', '238, 374, 100'),
(4, 'JOE THE PLUMBER JEANS', '1, 5', '27', '238, 374');
DECLARE #CATS TABLE (ID int, DESCR varchar(100));
INSERT #CATS (ID, DESCR) VALUES
(1, 'MEN'),
(2, 'WOMEN'),
(3, 'UNISEX'),
(4, 'KIDS'),
(5, 'TEENS'),
(6, 'BACK TO SCHOOL');
DECLARE #SUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBCATS (ID, DESCR) VALUES
(10, 'FOOTWEAR'),
(12, 'OUTERWEAR'),
(14, 'SWIMWEAR'),
(15, 'HOODIES'),
(27, 'CLOTHING'),
(35, 'SPORTS');
DECLARE #SUBSUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBSUBCATS (ID, DESCR) VALUES
(100, 'RUNNING'),
(104, 'ZIP TOPS'),
(135, 'FLEECE'),
(157, 'BIKINIS'),
(238, 'PANTS'),
(374, 'JOGGERS');
;
with prod as (
SELECT p.ID,
p.DESCRIPTION
--CONCAT(cat1.DESCR, ' > ', cat2.DESCR, ' > ', cat3.DESCR) AS CATEGORIES
, c.value as CatId
, c.[key] as CatKey
, sc.value as SubCatId
, sc.[key] as SubCatKey
, ssc.value as SubSubCatId
, ssc.[key] as SubSubCatKey
FROM #PRODUCTS p
cross apply OPENJSON(CONCAT('["', REPLACE(cat, ', ', '","'), '"]')) c
cross apply OPENJSON(CONCAT('["', REPLACE(subcat, ', ', '","'), '"]')) sc
cross apply OPENJSON(CONCAT('["', REPLACE(subsubcat, ', ', '","'), '"]')) ssc
where c.[key] = sc.[key]
and c.[key] = ssc.[key]
)
, a as (
select p.ID
, p.DESCRIPTION
, c.DESCR + ' > ' + sc.DESCR + ' > ' + ssc.DESCR as CATEGORIES
, p.CatKey
from prod p
inner join #CATS c on c.ID = p.CatId
inner join #SUBCATS sc on sc.ID = p.SubCatId
inner join #SUBSUBCATS ssc on ssc.ID = p.SubSubCatId
)
select DISTINCT ID
, DESCRIPTION
, replace(STUFF((SELECT distinct ' | ' + a2.CATEGORIES
from a a2
where a.ID = a2.ID
FOR XML PATH(''))
,1,2,''), '>', '>') CATEGORIES
from a
Totally separate answer because of the change to older technology. I think my original answer is still good for folks using current SQL Server versions, so I don't want to remove it.
I don't remember where I got the function. When I found it today it was named split_delimiter. I changed the name, added some comments, and incorporated the ability to have a delimiter that is more than one character long.
CREATE FUNCTION [dbo].[udf_split_string](#delimited_string VARCHAR(8000), #delimiter varchar(10))
RETURNS TABLE AS
RETURN
WITH cte10(num) AS ( -- 10 rows
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
, cte100(num) AS ( -- 100 rows
SELECT 1
FROM cte10 t1, cte10 t2
)
, cte10000(num) AS ( -- 10000 rows
SELECT 1
FROM cte100 t1, cte100 t2
)
, cte1(num) AS ( -- 1 row per character
SELECT TOP (ISNULL(DATALENGTH(#delimited_string), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM cte10000
)
, cte2(num) AS ( -- locations of strings
SELECT 1
UNION ALL
SELECT t.num + len(replace(#delimiter, ' ', '_'))
FROM cte1 t
WHERE SUBSTRING(#delimited_string, t.num, len(replace(#delimiter, ' ', '_'))) = #delimiter
)
, cte3(num, [len]) AS (
SELECT t.num
, ISNULL(NULLIF(CHARINDEX(#delimiter, #delimited_string, t.num), 0) - t.num, 8000)
FROM cte2 t
)
SELECT [Key] = ROW_NUMBER() OVER (ORDER BY t.num)
, [Value] = SUBSTRING(#delimited_string, t.num, t.[len])
FROM cte3 t;
GO
DECLARE #PRODUCTS TABLE (ID int, DESCRIPTION varchar(50), CAT varchar(30), SUBCAT varchar(30), SUBSUBCAT varchar(30));
INSERT #PRODUCTS (ID, DESCRIPTION, CAT, SUBCAT, SUBSUBCAT) VALUES
(1, 'NIKE MILLENIUM', '1, 2', '10, 12', '100, 135'),
(2, 'NIKE CORTEZ', '1, 5', '12, 15', '104, 374'),
(3, 'ADIDAS PANTS', '2, 6, 1', '27, 35, 10', '238, 374, 100'),
(4, 'JOE THE PLUMBER JEANS', '1, 5', '27', '238, 374');
DECLARE #CATS TABLE (ID int, DESCR varchar(100));
INSERT #CATS (ID, DESCR) VALUES
(1, 'MEN'),
(2, 'WOMEN'),
(3, 'UNISEX'),
(4, 'KIDS'),
(5, 'TEENS'),
(6, 'BACK TO SCHOOL');
DECLARE #SUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBCATS (ID, DESCR) VALUES
(10, 'FOOTWEAR'),
(12, 'OUTERWEAR'),
(14, 'SWIMWEAR'),
(15, 'HOODIES'),
(27, 'CLOTHING'),
(35, 'SPORTS');
DECLARE #SUBSUBCATS TABLE (ID int, DESCR varchar(100));
INSERT #SUBSUBCATS (ID, DESCR) VALUES
(100, 'RUNNING'),
(104, 'ZIP TOPS'),
(135, 'FLEECE'),
(157, 'BIKINIS'),
(238, 'PANTS'),
(374, 'JOGGERS');
;
with prod as (
SELECT p.ID,
p.DESCRIPTION
, c.value as CatId
, c.[key] as CatKey
, sc.value as SubCatId
, sc.[key] as SubCatKey
, ssc.value as SubSubCatId
, ssc.[key] as SubSubCatKey
FROM #PRODUCTS p
cross apply dbo.udf_split_string(cat, ', ') c
cross apply dbo.udf_split_string(subcat, ', ') sc
cross apply dbo.udf_split_string(subsubcat, ', ') ssc
where c.[key] = sc.[key]
and c.[key] = ssc.[key]
)
, a as (
select p.ID
, p.DESCRIPTION
, c.DESCR + ' > ' + sc.DESCR + ' > ' + ssc.DESCR as CATEGORIES
, p.CatKey
from prod p
inner join #CATS c on c.ID = p.CatId
inner join #SUBCATS sc on sc.ID = p.SubCatId
inner join #SUBSUBCATS ssc on ssc.ID = p.SubSubCatId
)
select DISTINCT ID
, DESCRIPTION
, replace(STUFF((SELECT distinct ' | ' + a2.CATEGORIES
from a a2
where a.ID = a2.ID
FOR XML PATH(''))
,1,2,''), '>', '>') CATEGORIES
from a
Well that should do work, i changed your character ">" for "-" just for see the data more simple.
the design of your tables is not perfect but the first try almost never is.
select mainp.ID, mainp.DESCRIPTION, stuff(ppaths.metapaths, len(ppaths.metapaths),1,'') metalinks
from #PRODUCTS mainp
cross apply(
select
(select
c.DESCR + '-' + sc.DESCR + '-' + sbc.DESCR + '|'
from #PRODUCTS p
cross apply (select row_number() over(order by Value) id, Value from split(p.CAT, ','))cat_ids
inner join #cats c on c.ID = cat_ids.Value
cross apply (select row_number() over(order by Value) id, Value from split(p.SUBCAT, ','))subcat_ids
inner join #SUBCATS sc on sc.ID = subcat_ids.Value
and subcat_ids.id = subcat_ids.id
cross apply (select row_number() over(order by Value) id, Value from split(p.SUBSUBCAT, ','))subsubcat_ids
inner join #SUBSUBCATS sbc on sbc.ID = subsubcat_ids.Value
and subsubcat_ids.id = subcat_ids.id
where p.id = mainp.ID
for xml path('')) metapaths
) ppaths
the link for split function
https://desarrolladores.me/2014/03/sql-server-funcion-split-para-dividir-un-string/

Pivot values on column based on grouped columns in SQL

I want to pivot values to columns based on a group. However, I do not know the values beforehand.
A query gives me this result.
Id Code EntityId
----------- ------------ -------------
3 22209776 1
4 143687971 3
4 143687971 4
4 143687971 5
4 143687971 15
5 143658155 7
5 143658155 8
I would like to output this
Id Code EntityId1 EntityId2 EntityId3 EntityId4
----------- ------------ ------------- ------------- ------------- -------------
3 22209776 1 NULL NULL NULL
4 143687971 3 4 5 15
5 143658155 7 8 NULL NULL
If you do now know how many column you are going to have in the result, you need to use dynamic T-SQL statement to build the PIVOT. For example:
IF OBJECT_ID('tempdb..#DataSource') IS NOT NULL
BEGIN;
DROP TABLE #DataSource;
END;
CREATE TABLE #DataSource
(
[id] INT
,[Code] INT
,[EntityId] INT
);
DECLARE #DynamicTSQLStatement NVARCHAR(MAX)
,#Columns NVARCHAR(MAX);
DECLARE #MaxColumns INT;
INSERT INTO #DataSource ([id], [Code], [EntityId])
VALUES (3, 22209776 , 1)
,(4, 143687971, 3)
,(4, 143687971, 4)
,(4, 143687971, 5)
,(4, 143687971, 15)
,(5, 143658155, 7)
,(5, 143658155, 8)
,(4, 143687971, 25)
,(4, 143687971, 26);
-- we need to know how many columns are going to be shown
SELECT TOP 1 #MaxColumns = COUNT(*)
FROM #DataSource
GROUP BY [Code]
ORDER BY COUNT(*) DESC;
-- we are building here the following string '[1],[2],[3],[4],[5],[6]';
-- this will change depending the input data
WITH gen AS
(
SELECT 1 AS num
UNION ALL
SELECT num+1
FROM gen
WHERE num+1<=#MaxColumns
)
SELECT #Columns = STUFF
(
(
SELECT ',[EntityId' + CAST([num] AS VARCHAR(12)) + ']'
FROM gen
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
)
OPTION (maxrecursion 10000);
SET #DynamicTSQLStatement = N'
SELECT *
FROM
(
SELECT [id]
,[Code]
,[EntityId]
,''EntityId'' + CAST(ROW_NUMBER() OVER(PARTITION BY [Code] ORDER BY [EntityId]) AS VARCHAR(12))
FROM #DataSource
) DS ([id], [Code], [EntityId], [RowID])
PIVOT
(
MAX([EntityId]) for [RowID] in (' + #Columns +')
) PVT;';
EXEC sp_executesql #DynamicTSQLStatement;
You could try using the pivot function:
declare #tmp TABLE (id int, Code int, EntityId NVARCHAR(10))
insert into #tmp (id, Code, EntityId)
values (3, 22209776 , 1),
(4, 143687971, 3),
(4, 143687971, 4),
(4, 143687971, 5),
(4, 143687971, 15),
(5, 143658155, 7),
(5, 143658155, 8)
select
pvt.id
,pvt.Code
,[1] as EntityID1
,[2] as EntityID2
,[3] as EntityID3
,[4] as EntityID4
from (
select
id, Code, EntityId
,ROW_NUMBER() over(partition by code order by EntityId) as RowNum
from
#tmp
) a
pivot (MAX(EntityId) for RowNum in ([1], [2], [3], [4])) as pvt

SQL Server: how to form a crosstab

I am using SQL Server 2014.
How can I create a cross-table out of the straight table like the below?
Code here where I try to do the multiple-pivoting.
Sample input
Code here only about the sample data.
GOAL after pivoting and reformatting of the columns by other columns
This logic would work for you:
Create an additional temp table where you store all the possible combinations of Store and City, then left join with your existing table.
From this left join you get the "calculated" string that you will use as a column name (C1_L_xCount, C1_L2_xCount, etc).
Then apply a pivot as below:
select ylabel.colx, ylabel.coly, y.myid, y.week, isnull(y.xCount, 0) xCount, isnull(y.yCount, 0) yCount
into #table
from (
select distinct y3.week, y1.city, y2.store, y1.city + '_' + y2.store + '_xCount' as colx, y1.city + '_' + y2.store + '_yCount' as coly
from #yt y1 cross join #yt y2 cross join #yt y3
) ylabel left join #yt y on y.week = ylabel.week and y.store = ylabel.store and y.city = ylabel.city
select * from #table --this is the additional table, the one used for pivoting
--this is your solution:
select myid
, week
, isnull(C1_L_xCount, 9) C1_L_xCount
, isnull(C1_L2_xCount, 9 ) C1_L2_xCount
, isnull(C2_L_xCount, 0) C2_L_xCount
, isnull(C2_L2_xCount, 0) C2_L2_xCount
, isnull(C1_L_yCount, 0) C1_L_yCount
, isnull(C1_L2_yCount, 0) C1_L2_yCount
, isnull(C2_L_yCount, 0) C2_L_yCount
, isnull(C2_L2_yCount, 0) C2_L2_yCount
from
(
select *
from #table
pivot ( max(xCount) for colx in ( [C1_L_xCount], [C1_L2_xCount],[C2_L_xCount], [C2_L2_xCount])) p
pivot ( max(yCount) for coly in ( [C1_L_yCount], [C1_L2_yCount],[C2_L_yCount], [C2_L2_yCount])) q
where myid is not null
) t
Please check a working demo here.
But, if you need to dynamically add Stores and Cities, you will need to convert this into a dynamic pivot.
We repeat the case such that
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
I changed the valuations a little bit to get more combinations, code here.
or code here:
CREATE TABLE #yt
([MyID] int, [Store] nvarchar(300), [City] nvarchar(300), [Week] int, [xCount] int, [yCount] int)
;
INSERT INTO #yt
([MyID], [Store], [City], [Week], [xCount], [yCount])
VALUES
(1, 'L', 'C1', 1, 96, 7),
(2, 'L', 'C1', 1, 138, 77),
(3, 'L2', 'C1', 1, 37, 744),
(4, 'L', 'C1', 1, 59, 74),
(5, 'L', 'C1', 2, 282,73333),
(6, 'L2', 'C2', 2, 212,7333),
(7, 'L2', 'C2', 2, 78,733),
(8, 'L', 'C2', 2, 97,73),
(9, 'L', 'C2', 3, 60,72222),
(10, 'L2', 'C2', 3, 123,7222),
(11, 'L2', 'C1', 3, 220,722),
(12, 'L2', 'C1', 3, 87,72)
;
select [MyId], [Week], [LC1_xCount], [LC2_xCount], [L2C1_xCount], [L2C2_xCount]
, [LC1_yCount], [LC2_yCount], [L2C1_yCount], [L2C2_yCount]
from
(
select myid, week, store, city, xcount, ycount,
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[xCount]
END
AS 'LC1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[xCount]
END
AS 'LC2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C1_xCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[xCount]
END
AS 'L2C2_xCount',
CASE WHEN
[City]='C1' AND [Store]='L'
THEN
[yCount]
END
AS 'LC1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L'
THEN
[yCount]
END
AS 'LC2_yCount',
CASE WHEN
[City]='C1' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C1_yCount',
CASE WHEN
[City]='C2' AND [Store]='L2'
THEN
[yCount]
END
AS 'L2C2_yCount'
from #yt
GROUP BY myid, week, store,city, xcount, ycount
) src;

how to get table from first table when data is not there in second table

i have requirement where i need to show data of both tables when both the ID's are same.when id is present in first table and not there in second table i need to show data from first table
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Test_History] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC01347E88 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'I', CAST(0x0000A4BC0134A390 AS DateTime))
INSERT [dbo].[Test_History] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'A', CAST(0x0000A4BC01391FCC AS DateTime))
/****** Object: Table [dbo].[TEST] Script Date: 06/19/2015 19:01:49 ******/
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (2, N'Rohan', N'I', CAST(0x0000A4BC0138D584 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (1, N'Mohan', N'A', CAST(0x0000A4BC013072DC AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (3, N'Raj', N'A', CAST(0x0000A4BC0138DED7 AS DateTime))
INSERT [dbo].[TEST] ([ID], [Name], [Status], [CreatedDate]) VALUES (4, N'Krishna', N'A', CAST(0x0000A4BC0138EE31 AS DateTime))
so far i have tried my query to achieve the result
select T.ID,COALESCE(T.ID,TT.ID),T.Name,COALESCE(T.Name,TT.Name),T.status,COALESCE(T.status,TT.status)
from Test T LEFT JOIN (Select TOP 1 ID,MIN(Name)name,Status from Test_History
GROUP BY ID,status
)TT
ON T.ID = TT.ID
where T.ID = 3
Id = 1 and 2 present show i will get data from both tables
Id = 3 and 4 not present in the table
so using coalesce i will get the data
from first table and show in 2nd table column also
but is there any other way like both tables are same structure
i'm thinking of
Declare #tablename varchar(10)
IF EXISTS (SELECT 1 from TESt where id = #id)
IF COunt there in both tables
SET #tablename = Test
ELSE
SET #tablename = Test_history
select * from #tablename where id = #ID
can i get any solution like this
You can use EXCEPT.
Here is an example:
SELECT a,b
FROM (
VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)
) AS MyTable(a, b)
EXCEPT
SELECT a,b
FROM (
VALUES (1, 2), (7, 8), (9, 10)
) AS MyTable(a, b);
This will return all rows of the upper statement, which are not in the second statement.
First: Thanks for the excellent setup for the data related to the question!
If your real question was if table variables can be used as described in your question, the answer is no; or more accurately that its not worth it.
Not recommended:
declare #TableName TABLE (
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL)
IF EXISTS (SELECT 1 from TESt where id = #id)
INSERT INTO #TableName SELECT * FROM dbo.TEST WHERE ID = #ID
ELSE INSERT INTO #TableName SELECT * FROM dbo.[Test_History] WHERE ID = #ID
select * from #tablename where id = #ID
Here's the solution I prefer:
DECLARE #ID INT = 3;
SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id
UNION ALL SELECT * FROM [dbo].[Test_History] th WHERE th.id = #id
and not exists ( SELECT * FROM [dbo].[TEST] ss WHERE ss.id = #id);
UNION ALL performs surprisingly well - don't forget the ALL keyword, and I am assuming that ID is a PK or AK.
If I'm understanding correctly and you want to display all records that match between the two tables and only records from first table when the id does not exist in the second in the same result set, then all you need is a simple left join:
SELECT *
FROM dbo.test t
LEFT OUTER JOIN Test_History th
ON t.id = th.id
WHERE t.id = #id