SQL Server tough Query - sql

I'm having an issue writing a tough query. I have the following table (as an example)
fusionId | productId | departmentId
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 3 | 2
I want a query that I can pass two departmentId's to (1,2) and for the query to return the productId only if both departmentId match the same productId
So for example if I sent the departmentId's 1 & 2 I would get the result
productId
1
Is this possible?

SELECT productId
FROM YourTable
WHERE departmentId IN (1,2)
GROUP BY productId
HAVING COUNT(DISTINCT departmentId) = 2
Or
SELECT productId
FROM YourTable
WHERE departmentId = 1
INTERSECT
SELECT productId
FROM YourTable
WHERE departmentId = 2

Create Table Temporary
(
fusionId int,
productId int,
departmentId int,
)
insert into Temporary values (1,1,1),(2,1,2),(3,2,1),(4,3,2)
Select productId from Temporary
Where productId in (Select productId from Temporary where departmentId = 1) and departmentId =2

SELECT productId
FROM YourTable
WHERE departmentId IN (1,2)
GROUP bY productId
HAVING COUNT(productId) > 1

Related

Is there some possibility to create PIVOT with gradual subtracting of values ​in individual columns?

Could somebody advice me, how to create PIVOT with some calculation?
Simple PIVOT with "real data" isn't problem, but I have no idea how to include some calculation even so basic like for example subtraction of values between some columns...
Please, if You'll be somebody able, try to use example of Tables and Data below...
-- Structure
-- ---------
-- Table of Products
CREATE TABLE ListProducts
(
ProductID INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(25)
);
-- Table of Processes
CREATE TABLE ListProcesses
(
ProcessID INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(25)
);
-- DataTable
CREATE TABLE Production
(
ProductionID INT IDENTITY(1,1) PRIMARY KEY,
ProductID INT FOREIGN KEY REFERENCES ListProducts(ProductID),
ProcessID INT FOREIGN KEY REFERENCES ListProcesses(ProcessID),
Amount INT
);
-- Data
-- ----
INSERT INTO ListProducts(Name) VALUES ('Product1'),('Product2'),('Product3');
INSERT INTO ListProcesses(Name) VALUES ('Process1'),('Process2'),('Process3');
INSERT INTO Production(ProductID,ProcessID,Amount) VALUES
(1,1,25),(2,1,15),(3,1,20),(1,2,10),(2,2,10),(3,2,5),(1,3,5),(2,3,5);
I would like to get PIVOT like:
| Products | Process1 | Process2 | Process3 |
| Product1 | 15 | 5 | 5 |
| Product2 | 5 | 5 | 5 |
| Product3 | 15 | 5 | NULL |
As You can see - output isn't like simple PIVOT but I have every 'previous' column subtracted by 'next' column (except of last column of course)...
Thank You a lot! :)
You can use conditional aggregation:
select lpr.name,
sum(case when p.processid = 1 then p.amount when p.processid = 2 then -p.amount end) as process1,
sum(case when p.processid = 2 then p.amount when p.processid = 3 then -p.amount end) as process2,
sum(case when p.processid = 3 then p.amount end) as process3
from production p join
listproducts lpr
on p.productid = lpr.productid join
listprocesses lpro
on p.processid = lpro.processid
group by lpr.name;
Here is a db<>fiddle.
You can write your query like following using PIVOT and LEAD.
select [name] as Products, Process1, Process2, Process3
from (select t1.Name,
t2.amount - Isnull((Lead(t2.amount)
over(partition by T2.productid
order by t2.processid ) ), 0) amount,
t3.Name as ProcesName
from listproducts t1
inner join production t2
ON t1.productid = t2.productid
inner join listprocesses t3
ON t3.processid = t2.processid) t
pivot ( max(amount)
for procesname in ([Process1],
[Process2],
[Process3]) ) pvt
Online Demo
you can try below using lead() function
DEMO
with cte as
(
select *,case when processname<>'process3' then
amount-coalesce(lead(amount) over(partition by productname order by processname),0) else amount end as amt from
(
select b.name as productname, c.name as processname,amount
from Production a
inner join ListProducts b on a.productid=b.ProductID
inner join ListProcesses c on a.ProcessID=c.ProcessID
)A
)
select productname, max(case when processname='Process1' then amt end) as process1,
max(case when processname='Process2' then amt end) process2,
max(case when processname='Process3' then amt end) process3
from cte
group by productname
OUTPUT:
productname process1 process2 process3
Product1 15 5 5
Product2 5 5 5
Product3 15 5

Count Values associated with key in Sql Server

I have three tables
Table Category
CategoryID Category
1 Climate
2 Area
Table CategoryDetail
DetailID CategoryID Desc
1 1 Hot
2 1 Cold
3 2 Area1
Table CategoryDetailValues
PK AnotherFK CategoryDetailID
1 1 1
2 1 1
3 1 2
4 2 1
Here AnotherFK is foreign key referring to another table. In record 1 and 2 duplicate exists that's ok but AnotherFK 1 has reference of CategoryDetailID 1 and 2 which has categoryID of 1 which is not ok
So from above tables
this result is valid from above three table
PK AnotherFK CategoryID DetailID Desc
1 1 1 1 Hot
2 1 1 1 Hot
But below result is not valid
PK AnotherFK CategoryID DetailID Desc
2 1 1 1 Hot
3 1 1 2 Cold
I can not put same AnotherFK in two different DetailID which has same CategoryID. I could have eliminated this by introducing CategoryID in CategoryDetailValues table and creating unique constraint but I am not allowed to do so.
Now my aim is to find all those record in CategoryDetailValues table which has different DetailID that are associated with same CategoryID. So that I can delete them.
Trying to achieve this in SQL Server 2012.
If your goal is to highlight all AnotherFK cases that have the same CategoryID, but differenty DetailIDs, the following ought to do the trick (pseudo-code):
SELECT * FROM (SELECT AnotherFK, ROW_NUMBER() OVER
(ORDER BY AnotherFK, CategoryID) AS rn FROM #myTable) AS a
WHERE rn > 1
Sample code:
CREATE TABLE #myTable
(
AnotherFK int
, CategoryID int
, DetailID int
) ;
INSERT INTO #myTable (
AnotherFK
, CategoryID
, DetailID
)
VALUES (1, 1, 1)
, (1, 1, 2);
SELECT * FROM (SELECT AnotherFK, ROW_NUMBER() OVER (ORDER BY AnotherFK, CategoryID) AS rn FROM #myTable) AS a
WHERE rn > 1
DROP TABLE #myTable
If this is not what you are after, please elaborate
I think you could use something like this:
Script to create sample tables:
CREATE TABLE mytable(
PK INTEGER NOT NULL PRIMARY KEY
,AnotherFK INTEGER NOT NULL
,CategoryDetailID INTEGER NOT NULL
);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (1,1,1);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (2,1,1);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (3,1,2);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (4,2,1);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (5,1,3);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (6,1,3);
INSERT INTO mytable(PK,AnotherFK,CategoryDetailID) VALUES (7,1,3);
CREATE TABLE mytable2(
DetailID INTEGER NOT NULL
,CategoryID INTEGER NOT NULL
,Descr VARCHAR(5) NOT NULL
);
Query to show "suspect" record (I think you have to decide what records delete...):
SELECT * FROM (
SELECT * ,COUNT(*) OVER (PARTITION BY CategoryID, ANotherFK) AS X
, COUNT(*) OVER (PARTITION BY CategoryID, DetailID, ANotherFK) AS X1
FROM mytable A
INNER JOIN mytable2 B ON A.CategoryDetailID= B.DetailID
)C
WHERE X-X1 >0
Output:
+--+----+-----------+------------------+----------+------------+-------+---+----+
| | PK | AnotherFK | CategoryDetailID | DetailID | CategoryID | Descr | X | X1 |
+--+----+-----------+------------------+----------+------------+-------+---+----+
| | 1 | 1 | 1 | 1 | 1 | Hot | 3 | 2 |
| | 2 | 1 | 1 | 1 | 1 | Hot | 3 | 2 |
| | 3 | 1 | 2 | 2 | 1 | Cold | 3 | 1 |
+--+----+-----------+------------------+----------+------------+-------+---+----+
This query will look in Categorydetail for records with duplicate DetailID. Than join the tables to provide you the details. It's still up to you to decide which records should be deleted.
select *
from(
Select CategoryID
from CategoryDetail
group by CategoryID
having count(DetailID)>1)aggr
join CategoryDetail c on aggr.CategoryID = c.CategoryID
join CategoryDetailValues v on c.CategoryDetailID = v.CategoryDetailID
You want one value per AnotherFK and Category. So the third table should have a composite key:
CategoryDetailValues(AnotherFK, CategoryID, DetailID, HowMany)
with a unique constraint on AnotherFK, CategoryID and both building a foreign key to CategoryDetail(CategoryID, DetailID).
In order to clean up data first, you'd have to look for ambiguities:
select AnotherFK, CategoryID, DetailID
from
(
select
cdv.AnotherFK, cd.CategoryID, cdv.DetailID,
count(distinct cd.DetailID) over (partition by cdv.AnotherFK, cd.CategoryID) as cnt
from CategoryDetailValues cdv
join CategoryDetail cd on cd.DetailID = cdv.CategoryDetailID
)
where cnt > 1
order by AnotherFK, CategoryID, DetailID
You could try this solution that comes with following assumption: within CDV table, for every [AnotherFK] value (ex. 1) should be displayed only those rows with the minimum [CategoryDetailID] (ex. 1)
SELECT *
FROM (
SELECT cdv.PK, cdv.AnotherFK, cd.CategoryID, cd.[Desc],
Rnk = DENSE_RANK() OVER(PARTITION BY cdv.AnotherFK ORDER BY cdv.CategoryDetailID)
FROM dbo.CategoryDetailValues cdv
JOIN dbo.CategoryDetail cd ON cd.DetailID = cdv.DetailID
WHERE cdv.AnotherFK = 1
) x
WHERE x.Rnk = 1

How to get duplicate rows inclusive of first row number in SQL Server?

I have written below query to retrieve duplicate customers using Row_Number() in SQL Server.
Cust_PKID ---------------+ CustomerID ----------------- + MobileNo
1 | A00001 | 9000477444
2 | A00002 | 9000477444
3 | A00003 | 9000477444
Query:-
Select TMP.CustID
From
(
Select CustomerID CustID,
Row_Number() Over(Partition By MobileNo Order By (Select Null)) As RowNo
From dbo.Customers
) TMP
Where TMP.RowNo > 1
Output:-
Cust_PKID ---------------+ CustomerID ----------------- + MobileNo
2 | A00002 | 9000477444
3 | A00003 | 9000477444
How can I retrieve records including of first RowNo record in single select statement?
You are looking for COUNT() OVER() window function not ROW_NUMBER
Select TMP.CustID
From
(
Select CustomerID CustID,
COUNT(1) Over(Partition By MobileNo) As RowNo
From dbo.Customers
) TMP
Where TMP.RowNo > 1
This will bring all the duplicate MobileNo records

SQL Query using Partition By

I have following table name JobTitle
JobID LanaguageID
-----------------
1 1
1 2
1 3
2 1
2 2
3 4
4 5
5 2
I am selecting all records from table except duplicate JobID's for which count > 1. I am selecting only one record/first row from the duplicate JobID's.
Now I am passing LanguageID as paramter to stored procedure and I want to select duplicate JobID for that languageID along with the other records Also.
If I have passed languageID as 1 then output should come as follows
JobID LanaguageID
-----------------
1 1
2 1
3 4
4 5
5 2
I have tried using following query.
with CTE_RN as
(
SELECT ROW_NUMBER() OVER(PARTITION BY JobTitle.JobID ORDER BY JobTitle.JobTitle) AS RN
FROM JobTitle
INNER JOIN JobTitle_Lang
ON JobTitle.JobTitleID = JobTitle_Lang.JobTitleID
)
But I am unable to use WHERE clause in the above query.
Is any different approch should be followed. Or else how can i modify the query to get the desired output
with CTE_RN as
(
SELECT
JobID, LanaguageID,
ROW_NUMBER() OVER(PARTITION BY JobTitle.JobID ORDER BY JobTitle.JobTitle) AS RN
FROM JobTitle
INNER JOIN JobTitle_Lang ON JobTitle.JobTitleID = JobTitle_Lang.JobTitleID
)
select
from CTE_RN
where RN = 1 or LanguageID = #LanguageID
update
simplified a bit (join removed), but you'll get the idea:
declare #LanguageID int = 2
;with cte_rn as
(
select
JobID, LanguageID,
row_number() over(
partition by JobTitle.JobID
order by
case when LanguageID = #LanguageID then 0 else 1 end,
LanguageID
) as rn
from JobTitle
)
select *
from cte_rn
where rn = 1
sql fiddle demo
SELECT b.[JobID], b.[LanaguageID]
FROM
(SELECT a.[JobID], a.[LanaguageID],
ROW_NUMBER() OVER(PARTITION BY a.[JobID] ORDER BY a.[LanaguageID]) AS [row]
FROM [JobTitle] a) b
WHERE b.[row] = 1
Result
| JOBID | LANAGUAGEID |
--------|-------------|
| 1 | 1 |
| 2 | 1 |
| 3 | 4 |
| 4 | 5 |
| 5 | 2 |
See a demo

Tsql looping father-son relationship between tables

I have a table like this:
table item
(
id int,
quantity float,
father int, -- refer to item itself in case of subitem
)
I need to sum al quantity plus sons quantity like this way:
select i.id, max(i.quantity)+sum(ft.quantity) as quantity
from item i
left join item ft on ft.id=i.id
group by i.id
My trouble is because relationship between father-son is recursive so I would like to sum also his grandfather quantity and so on... and i don't know the maximum deepness, than I can not join many times.
What can i do?
Thank you.
You have to use a recursive CTE. Somthing like this:
;WITH FathersSonsTree
AS
(
SELECT Id, quantity, 0 AS Level
FROM Items WHERE fatherid IS NULL
UNION ALL
SELECT c.id, c.quantity, p.level+1
FROM FathersSonsTree p
INNER JOIN items c ON c.fatherid = p.id
), ItemsWithMaxQuantities
AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY level
ORDER BY quantity DESC) rownum
FROM FathersSonsTree
)
SELECT
ID,
(SELECT MAX(Quantity)
FROM FathersSonsTree t3
WHERE t3.level = t1.level
) +
ISNULL((SELECT SUM(t2.Quantity)
FROM FathersSonsTree t2
WHERE t1.level - t2.level = 1), 0)
FROM FathersSonsTree t1
ORDER BY ID;
SQL Fiddle Demo
This will give you something like:
| ID | QUANTITY |
-----------------
| 1 | 10 |
| 2 | 20 |
| 3 | 20 |
| 4 | 20 |
| 5 | 32 |
| 6 | 32 |
| 7 | 32 |
| 8 | 32 |
You might try building a recursive CTE (common table expression) as described in this article on SQLAuthority:
http://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hierarchical-query-using-a-recursive-cte-a-primer/
The author, Pinal Dave, discusses using a recursive CTE on an employees table that has a self referencing foreign key for ManagerID to return a list of employees with a count of how many levels are between them and the top of the hierarchy where the employee has no manager (ManagerID = NULL). That's not exactly what you're wanting but it might get you started.
I did a little experimentation and ended up with something very similar to Mahmoud Gamal's solution but with a slight difference to include the not just the parent, grandparents, great-grandparents, etc. quantity but also the child quantity.
Here's the test table I used:
CREATE TABLE Items(ID int IDENTITY
CONSTRAINT PK_Items PRIMARY KEY,
Quantity int NOT NULL,
ParentID int NULL
CONSTRAINT FK_Item_Parents REFERENCES Items(ID));
And the data:
ID Quantity ParentID
------------------------------------------------------------
1 10 {NULL}
2 10 1
3 10 2
4 10 3
5 10 2
Here's my recursive query:
WITH cteRecursiveItems
AS (SELECT Id,
quantity,
0
AS Level
FROM Items
WHERE ParentID IS NULL
UNION ALL
SELECT i.id,
i.quantity,
cri.level + 1
FROM
cteRecursiveItems cri
INNER JOIN items i ON i.ParentID = cri.id)
SELECT ID,
Quantity + (
SELECT MAX(Quantity)
FROM cteRecursiveItems cri3
WHERE cri3.level = cri1.level) + (
SELECT SUM(cri2.Quantity)
FROM cteRecursiveItems cri2
WHERE cri1.level - cri2.level = 1) as Total
FROM cteRecursiveItems cri1
ORDER BY ID;
And here's the results I get from running it against the test table:
ID Total
----------------------------------------
1 {NULL}
2 30
3 30
4 40
5 30
It still needs a little tweaking because the first and 2nd row are off by 10. Row 1 should have a total of 10 and row 2 should have a total of 20. I'm making a note to try and fix that when I get home. Can't spend too much of my employer's time on this right now. :) The other rows have the value I was expecting.