how to get Related Records using LINQ - sql

Hello I have this table structure:
**ProductsTable**
ProductID
ProductName
**CategoriesTable**
CategoryID
CategoryName
**ProductCategories**
ProductID
CategoryID
Each Product can belong to multiple categories. Now I need a way to find Related Products. But I want it in this way:
Let's say Product1 belong to Laptop, Accessories. So only those products who belong to both categories (not just one) can be retrieved.
SQL Query will work, however if you can give LINQ it will be best.
Thanks in Advance
Marc V.

Update
Here is my sql solution with setup:
declare #p table(id int, name varchar(10))
declare #c table(id int, name varchar(10))
declare #pc table(pid int, cid int)
insert into #p (id, name) values (1, 'laptop')
insert into #p (id, name) values (2, 'desktop')
insert into #p (id, name) values (3, 'milk')
insert into #c (id, name) values (1, 'computer')
insert into #c (id, name) values (2, 'device')
insert into #c (id, name) values (3, 'food')
insert into #pc (pid, cid) values (1, 1)
insert into #pc (pid, cid) values (1, 2)
--insert into #pc (pid, cid) values (1, 3)
insert into #pc (pid, cid) values (2, 1)
insert into #pc (pid, cid) values (2, 2)
insert into #pc (pid, cid) values (3, 3)
declare #productId int;
set #productId = 1;
select *
from #p p
where
--count of categories that current product shares with the source product
--should be equal to the number of categories the source product belongs to
(
select count(*)
from #pc pc
where pc.pid = p.id
and pc.cid in (
select cid from #pc pc
where pc.pid = #productId
)
) = (select count(*) from #pc pc where pc.pid = #productId)
and
p.id <> #productId

Related

Matrix table SQL

I have three tables:
Table tCity
(Id int, City nvarchar(50))
Table tLocation
(Id int, Location nvarchar(50))
Table tCityLocation
(Id int, CityId int, LocationId int)
I would like to generate matrix table like in image below.
If City belongs to location-> in appropriate cell in table, char X will be written down.
I would like to about any sophisticated approach how to reach it. I had issue in similar logic and processed by iteration in cursors with dynamically added column to result set table.
Exists any sophisticated approach instead of cursor and dynamically added column?
Thank you.
It should be something like this:
DECLARE #DymanimcTSQLSatement NVARCHAR(MAX)
,#DynamicColumns NVARCHAR(MAX);
SET #DynamicColumns = STUFF
(
(
SELECT ',' + QUOTENAME([Location])
FROM tLocation
GROUP BY [Location]
ORDER BY [Location]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1
,1
,''
);
SET #DymanimcTSQLSatement = N'
SELECT *
FROM
(
SELECT L.Location
,C.City
,1
FROM tCityLocation CL
INNER JOIN tLocation L
ON CL.[LocationId] = L.[id]
INNER JOIN tCity C
ON CL.[CityId] = C.[id]
) DS (country, city, value)
PIVOT
(
MAX([value]) FOR [country] IN (' + #DynamicColumns +')
) PVT;';
EXECUTE sp_executesql #DymanimcTSQLSatement;
and here is the data I have used:
-- Creating Table tCity
CREATE TABLE tCity (
Id int,
City nvarchar(50)
);
-- Populating Table tCity with 10 records
INSERT INTO tCity (Id, City) VALUES (1, 'New York');
INSERT INTO tCity (Id, City) VALUES (2, 'Los Angeles');
INSERT INTO tCity (Id, City) VALUES (3, 'Paris');
INSERT INTO tCity (Id, City) VALUES (4, 'Tokyo');
INSERT INTO tCity (Id, City) VALUES (5, 'Sydney');
INSERT INTO tCity (Id, City) VALUES (6, 'Philadelphia');
INSERT INTO tCity (Id, City) VALUES (7, 'Rio de Janeiro');
INSERT INTO tCity (Id, City) VALUES (8, 'Cape Town');
INSERT INTO tCity (Id, City) VALUES (9, 'Beijing');
INSERT INTO tCity (Id, City) VALUES (10, 'Singapore');
-- Creating Table tLocation
CREATE TABLE tLocation (
Id int,
Location nvarchar(50)
);
-- Populating Table tLocation with 10 records
INSERT INTO tLocation (Id, Location) VALUES (1, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (2, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (3, 'France');
INSERT INTO tLocation (Id, Location) VALUES (4, 'Japan');
INSERT INTO tLocation (Id, Location) VALUES (5, 'Australia');
INSERT INTO tLocation (Id, Location) VALUES (6, 'United States');
INSERT INTO tLocation (Id, Location) VALUES (7, 'Brazil');
INSERT INTO tLocation (Id, Location) VALUES (8, 'South Africa');
INSERT INTO tLocation (Id, Location) VALUES (9, 'China');
INSERT INTO tLocation (Id, Location) VALUES (10, 'Singapore');
-- Creating Table tCityLocation
CREATE TABLE tCityLocation (
Id int,
CityId int,
LocationId int
);
INSERT INTO tCityLocation (Id, CityId, LocationId)
SELECT
ROW_NUMBER() OVER (ORDER BY tCity.Id),
tCity.Id, tLocation.Id
FROM tCity
JOIN tLocation
ON tCity.Id = tLocation.Id;
The idea is to built a dynamic PIVOT, where the PIVOT columns are the unique countries (in your case locations). If your SQL Server supports STRING_AGG, you can do this in different way:
SELECT #DynamicColumns = STRING_AGG(CAST(QUOTENAME([Location]) AS VARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY [Location])
FROM
(
SELECT DISTINCT [Location]
FROM tLocation
) DS ([Location]);
Better way for answer to this question use pivot
First I get list Location of table Location
Second I get list Location of table tLocation For use in select
Because I need to use ISNULL for replace null with space
after use pivot for get result
result :Rows include List City and Column include Location
city
Asia
CRezah Rep.
Europe
France
GB
Germany
Japan
N.America
USA
NY
x
x
Prague
x
x
x
London
x
x
Tokio
x
x
--Get List Columns for Pivot
DECLARE #cols AS NVARCHAR(MAX),#scols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(Location) from tLocation
FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'' )
--can to use below Code for Replace null with space
select #scols = STUFF((SELECT distinct ',ISNULL(' + QUOTENAME(Location) +','' '') as '+ QUOTENAME(Location)
from tLocation FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = 'SELECT city ,'+#scols+' from
(
select cl.CityId,c.City,l.Location,''X'' as Value from tCityLocation cl
inner join tCity c on cl.CityId=c.Id
inner join tLocation l on cl.LocationId=l.Id
) x
pivot
(
max( value) for Location in (' + #cols + ')
) p
order by CityId
'
execute(#query)
You can to insert the basic data with the following codes
create table tCity(Id int, City nvarchar(50))
create table tLocation(Id int, Location nvarchar(50))
create table tCityLocation(Id int, CityId int, LocationId int)
insert into tCity
(id,City)
select 1 as id,'NY' union all select 2 as id,'Prague'
union all select 3 as id,'London' union all select 4 as id,'Tokio'
insert into tLocation (id,Location)
select 1,'USA' union all select 2,'CRezah Rep.' union all select 3,'France' union all select 4,'GB'
union all select 5,'Germany' union all select 6,'Europe' union all select 7,'N.America'
union all select 9,'Asia' union all select 10,'Japan'
insert into tCityLocation
(id,CityId,LocationId)
select 1,1,1 union all select 2,1,7 union all select 3,2,2 union all select 4,2,6 union all select 5,3,4 union all
select 6,3,6 union all select 7,4,9 union all select 8,4,10

SQL Server : SELECT query to get DISTINCT and MAX display order value

I have a product table, Category table, and Mapping table. Category saved as a category tree. If a single product has mapped with the last category in a hierarchy of level three. All the levels saved in the mapping table with the same product id.
eg : Assume there is category tre like this Electronic>LapTops>DELL and when product id = 1 assigned to category 'DELL' mapping will save as [1,Electronic],[1,LapTops],[1,DELL]
When I get data with a select query all the category levels appear with the same product Id.
My problem is I need to retrieve data as [productId, ProductName, LastCategortLevel, CategoryName, CategoryId].
Refer actual result below. I just need to pick the highlighted product with the last category level which is the highest category order level.
I can't use another stored procedure or function because it's a small part of a large stored procedure.
The actual database tables are very big. But I have tried to implement the same scenario with small temp tables. see the below queries.
DECLARE #Products TABLE (ProductId INT NOT NULL)
INSERT INTO #Products(ProductId)
SELECT ProductId
FROM (VALUES (1), (2), (3), (4)) as x (ProductId)
DECLARE #Categories TABLE (CategoId INT NOT NULL,
Name VARCHAR(MAX) NOT NULL,
ParentCategoryId INT NOT NULL,
DisplayOrder INT NOT NULL)
-- 1st category tree
INSERT INTO #Categories VALUES (10, 'Electronic', 0, 1)
INSERT INTO #Categories VALUES (11, 'LapTops', 10, 2)
INSERT INTO #Categories VALUES (12, 'DELL', 11, 3)
INSERT INTO #Categories VALUES (13, 'HP', 11, 3)
-- 2st category tree
INSERT INTO #Categories VALUES (14, 'Clothes', 0, 1)
INSERT INTO #Categories VALUES (15, 'T-Shirts', 14, 2)
INSERT INTO #Categories VALUES (16, 'Red', 15, 3)
INSERT INTO #Categories VALUES (17, 'Denim', 14, 2)
INSERT INTO #Categories VALUES (18, 'Levise', 17, 3)
DECLARE #Product_Category_Mappings TABLE(MappingId INT NOT NULL,
ProductId INT NOT NULL,
CategoryId INT NOT NULL)
INSERT INTO #Product_Category_Mappings VALUES (100, 1, 10)
INSERT INTO #Product_Category_Mappings VALUES (101, 1, 11)
INSERT INTO #Product_Category_Mappings VALUES (102, 1, 12)
INSERT INTO #Product_Category_Mappings VALUES (103, 2, 10)
INSERT INTO #Product_Category_Mappings VALUES (104, 2, 11)
INSERT INTO #Product_Category_Mappings VALUES (105, 2, 12)
INSERT INTO #Product_Category_Mappings VALUES (106, 3, 14)
INSERT INTO #Product_Category_Mappings VALUES (107, 3, 15)
INSERT INTO #Product_Category_Mappings VALUES (108, 3, 16)
INSERT INTO #Product_Category_Mappings VALUES (109, 4, 14)
INSERT INTO #Product_Category_Mappings VALUES (110, 4, 17)
INSERT INTO #Product_Category_Mappings VALUES (111, 4, 18)
SELECT *
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
ORDER BY P.ProductId, C.DisplayOrder
Result of the above script. How I get highlighted rows?
For each ProductId, you want the row with highest DisplayOrder. You can use window functions:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY P.ProductId ORDER BY C.DisplayOrder DESC) rn
FROM #Products P
INNER JOIN #Product_Category_Mappings M ON M.ProductId = P.ProductId
INNER JOIN #Categories C ON C.CategoId = M.CategoryId
WHERE M.ProductId = P.ProductId
) t
WHERE rn = 1
ORDER BY P.ProductId, C.DisplayOrder

SQL Finding multiple combinations in 2 tables (with all records)

I have two tables, one with some user configurations (#USERCONFIG) and the other (#COMBINATIONS), multiples combinations of configurations I need to find in the first table.
CREATE TABLE #COMBINATIONS (INDEX1 INT, MENU CHAR(10))
CREATE TABLE #USERCONFIG (USERID VARCHAR(10), MENU VARCHAR(10))
INSERT INTO #COMBINATIONS VALUES (1, 'ABC300')
INSERT INTO #COMBINATIONS VALUES (1, 'ABC400')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC100')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC500')
INSERT INTO #COMBINATIONS VALUES (2, 'ABC600')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC500')
INSERT INTO #USERCONFIG VALUES ('SMITHJ', 'ABC600')
INSERT INTO #USERCONFIG VALUES ('SMITHC', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHC', 'ABC500')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC100')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC200')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC300')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC400')
INSERT INTO #USERCONFIG VALUES ('SMITHA', 'ABC600')
With this example data, I want the resultset to look like this:
'SMITHJ', '2'
'SMITHA', '1'
'SMITHC', '2'
Where it will return all users that have a match of configurations from the combinations table.
Any help would be appreciated.
The following will list users and the complete combinations they have. If it helps, you can think of it as the recipe-ingredient and user-ingredient textbook problem:
SELECT alluser.USERID, index_menu.INDEX1
FROM (SELECT DISTINCT USERID FROM #USERCONFIG) AS alluser
CROSS JOIN #COMBINATIONS AS index_menu
LEFT JOIN #USERCONFIG AS user_menu ON alluser.USERID = user_menu.USERID AND index_menu.MENU = user_menu.MENU
GROUP BY alluser.USERID, index_menu.INDEX1
HAVING COUNT(index_menu.MENU) = COUNT(user_menu.MENU)
This snippet will get that result:
IF OBJECT_ID('tempdb..#COMBINATIONS') IS NOT NULL DROP TABLE #COMBINATIONS;
IF OBJECT_ID('tempdb..#USERCONFIG') IS NOT NULL DROP TABLE #USERCONFIG;
CREATE TABLE #COMBINATIONS (INDEX1 INT, MENU VARCHAR(10));
CREATE TABLE #USERCONFIG (USERID VARCHAR(10), MENU VARCHAR(10));
INSERT INTO #COMBINATIONS (INDEX1, MENU) VALUES
(1, 'ABC301'),
(1, 'ABC401'),
(2, 'ABC102'),
(2, 'ABC502'),
(2, 'ABC602');
INSERT INTO #USERCONFIG (USERID, MENU) VALUES
('SMITHJ', 'ABC102'),
('SMITHJ', 'ABC502'),
('SMITHJ', 'ABC602'),
('SMITHC', 'ABC102'),
('SMITHC', 'ABC502'),
('SMITHA', 'ABC102'),
('SMITHA', 'ABC200'),
('SMITHA', 'ABC301'),
('SMITHA', 'ABC401'),
('SMITHA', 'ABC602');
SELECT USERID, INDEX1
FROM
(
SELECT uconf.USERID, comb.INDEX1,
COUNT(*) AS Total,
DENSE_RANK() OVER (PARTITION BY uconf.USERID ORDER BY COUNT(*) DESC, comb.INDEX1 ASC) AS Rnk
FROM #USERCONFIG uconf
INNER JOIN #COMBINATIONS comb
ON comb.MENU = uconf.MENU
GROUP BY uconf.USERID, INDEX1
) q
WHERE Rnk = 1
ORDER BY Total DESC, USERID;
Returns:
USERID INDEX1
SMITHJ 2
SMITHA 1
SMITHC 2

Existing SQL Server 2008 script improvement

SQL Server 2008
I have two tables with OrderIds and ItemIds. I need a resulting table with each OrderId from forst table linked with OrderId from second table where the number of identical ItemIds is maximum.
I did a script that does this using two loops but if the number of OrderIds in those tables is big (~1000) it means the loop has to be run 1000x1000 times, which might be too long. Ca this be achieved in a better way?
See my below my already written script:
drop table #Match, #OrderRec, #OrderSent
create table #Match(
OrderIdRec int NULL,
OrderIdSent int NULL)
create table #OrderRec(
OrderIdRec int NOT NULL,
ItemId int NULL)
create table #OrderSent(
OrderIdSent int NOT NULL,
ItemId int NULL)
insert #OrderRec values (1, 1)
insert #OrderRec values (1, 5)
insert #OrderRec values (1, 7)
insert #OrderRec values (1, 4)
insert #OrderRec values (1, 15)
insert #OrderRec values (1, 10)
insert #OrderRec values (2, 21)
insert #OrderRec values (2, 15)
insert #OrderRec values (2, 21)
insert #OrderRec values (2, 26)
insert #OrderRec values (5, 4)
insert #OrderRec values (5, 3)
insert #OrderRec values (5, 12)
insert #OrderRec values (5, 1)
insert #OrderSent values (121, 1)
insert #OrderSent values (121, 2)
insert #OrderSent values (121, 5)
insert #OrderSent values (121, 10)
insert #OrderSent values (121, 9)
insert #OrderSent values (122, 6)
insert #OrderSent values (122, 7)
insert #OrderSent values (122, 9)
insert #OrderSent values (122, 11)
insert #OrderSent values (142, 1)
insert #OrderSent values (142, 12)
insert #OrderSent values (142, 4)
insert #OrderSent values (142, 11)
set nocount on
declare #OrderIdRec int,
#OrderIdSent int,
#cnt numeric(10),
#cnt_max numeric(10),
#OrderIdSentMax int
select #OrderIdRec = MIN(OrderIdRec)
from #OrderRec
while ISNULL(#OrderIdRec,0) > 0
begin
select #OrderIdSent = MIN(OrderIdSent)
from #OrderSent
set #cnt_max = 0
set #OrderIdSentMax = NULL
while ISNULL(#OrderIdSent,0) > 0
begin
set #cnt = 0
select #cnt = COUNT(*)
from #OrderRec r
inner join #OrderSent t
on t.ItemId = r.ItemId
where r.OrderIdRec = #OrderIdRec
and t.OrderIdSent = #OrderIdSent
if isnull(#cnt, 0) > #cnt_max
begin
set #cnt_max = #cnt
set #OrderIdSentMax = #OrderIdSent
end
select #OrderIdSent = MIN(OrderIdSent)
from #OrderSent
where OrderIdSent > #OrderIdSent
end
insert #Match(
OrderIdRec,
OrderIdSent)
values (#OrderIdRec, #OrderIdSentMax)
select #OrderIdRec = MIN(OrderIdRec)
from #OrderRec
where OrderIdRec > #OrderIdRec
end
select *
from #Match
order by OrderIdRec
The actual script starts with set nocount on, what is before is just to create a set of data to play with.
The result is:
OrderIdRec OrderIdSent
1 121
2 NULL
5 142
;WITH s AS
(
SELECT OrderIdRec, OrderIdSent,
rn = ROW_NUMBER() OVER (PARTITION BY OrderIdRec ORDER BY c DESC)
FROM
(
SELECT r.OrderIdRec, s.OrderIdSent,
c = COUNT(*) OVER (PARTITION BY r.OrderIdRec, s.OrderIdSent)
FROM #OrderRec AS r
INNER JOIN #OrderSent AS s
ON r.ItemId = s.ItemId
) AS s2
),
d AS (SELECT OrderIdRec FROM #OrderRec GROUP BY OrderIdRec)
SELECT d.OrderIdRec, s.OrderIdSent
FROM d LEFT OUTER JOIN s
ON d.OrderIdRec = s.OrderIdRec AND s.rn = 1
ORDER BY d.OrderIdRec;
The following query gets the counts for all pairs between the two tables:
select orec.OrderId, osent.OrderId, count(*) as cnt
from OrderRec orec join
OrderSent osent
on orec.itemId = osent.itemId
group by orec.OrderId, osent.OrderId;
The following gets the highest cnt value for each orec.OrderId:
select oo.*
from (select orec.OrderId, osent.OrderId, count(*) as cnt,
row_number() over (partition by orec.OrderId, osent.OrderId order by count(*) desc
) as seqnum
from OrderRec orec join
OrderSent osent
on orec.itemId = osent.itemId
group by orec.OrderId, osent.OrderId
) oo
where seqnum = 1;

SQL CTE counting childs recursion

I'd like (using cte) to count children in table in that way to have at parent level number of all children including theirs children. Is there any sample available?
CREATE TABLE t_parent (id INT NOT NULL PRIMARY KEY, parentID INT NOT NULL)
INSERT
INTO t_parent
VALUES (1, 0)
INSERT
INTO t_parent
VALUES (2, 1)
INSERT
INTO t_parent
VALUES (3, 1)
INSERT
INTO t_parent
VALUES (4, 2)
INSERT
INTO t_parent
VALUES (5, 1)
INSERT
INTO t_parent
VALUES (6, 5)
INSERT
INTO t_parent
VALUES (7, 5);
WITH q AS
(
SELECT id, parentId
FROM t_parent
UNION ALL
SELECT p.id, p.parentID
FROM q
JOIN t_parent p
ON p.id = q.parentID
)
SELECT id, COUNT(*)
FROM q
GROUP BY
id