Retrieve matching rows using join - sql

This is a simplified version of my problem.
I have table like below
Id Name SNumber
100 XYZ 123
100 XYZ 123
101 ABC 123
103 QAZ 123
100 XYZ 971
100 XYZ 872
100 XYZ 659
102 PQR 145
102 PQR 707
103 QAZ 421
I want to count rows having Snumber as '123' ie Total column and rows having Snumber not as '123' i.e. otherTotal column
Id Name Total OtherTotal
100 XYZ 2 3
101 ABC 1 0
102 PQR 0 2
103 QAZ 1 1
What I am doing is using join
Select xx.*,otherTotal
From
( Select Id,Name,count(*) as Total
From table
Where Snumber like '123'
Group By id,name
)xx
Inner join
( Select Id,Name,count(*) as otherTotal
From table
Where Snumber not like '123'
Group By id,name
)yy
On xx.Id=yy.Id
But this will only return rows if particular Id has both Snumber as 123 and not as 123
Data returned is like below
Id Name Total OtherTotal
100 XYZ 2 3
103 QAZ 1 1
Now there is no guarntee that a particular Id will always have Snumber as 123 so I can't use Left or Right join. How to solve this quagmire ? Giggity

Try this:
SELECT id, name,
COUNT(CASE WHEN SNumber = 123 THEN 1 END) Total,
COUNT(CASE WHEN SNumber <> 123 THEN 1 END) OtherTotal
FROM t
GROUP BY id, name
ORDER BY id
Fiddle here.

select
Id, Name,
sum(case when SNumber = 123 then 1 else 0 end) as Total,
sum(case when SNumber <> 123 then 1 else 0 end) as OtherTotal
from Table1
group by Id, Name
order by Id
or
select
Id, Name,
count(*) - count(nullif(SNumber, 123)) as Total,
count(nullif(SNumber, 123)) as OtherTotal
from Table1
group by Id, Name
order by Id
sql fiddle demo

try this one.
DECLARE #TABLE TABLE (ID INT, NAME VARCHAR(40), SNUMBER INT)
INSERT INTO #TABLE
VALUES
(100 ,'XYZ', 123),
(100 ,'XYZ', 123),
(101 ,'ABC', 123),
(103 ,'QAZ', 123),
(100 ,'XYZ', 971),
(100 ,'XYZ', 872),
(100 ,'XYZ', 659),
(102 ,'PQR', 145),
(102 ,'PQR', 707),
(103 ,'QAZ', 421)
SELECT
ID,
NAME,
(
SELECT
COUNT(SNUMBER) FROM #TABLE B
WHERE
SNUMBER = '123' AND A.ID = B.ID
) AS TOTAL,
(
SELECT
COUNT(SNUMBER) FROM #TABLE B
WHERE
SNUMBER <> '123' AND A.ID = B.ID
) AS OTHERTOTAL
FROM
#TABLE A
GROUP BY ID, NAME

Related

How to update records belongs to same partition SQL Server

I have a table in a database containing the following data:
GroupId ExceptionId ParentExceptionId row
1 101 NULL 1
1 102 NULL 2
1 103 NULL 3
2 104 NULL 1
2 105 NULL 2
2 106 NULL 3
3 107 NULL 1
3 108 NULL 2`
I worte a following query to get the above row number:
with CTE_RN as
(
SELECT a.[GroupId], a.[SolId], a.[id],ParentExceptionId,
ROW_NUMBER() OVER(PARTITION BY a.[GroupId] ORDER BY a.[GroupId]) AS [row]
FROM [dbo].[trn_Report6_Zone1_Exception] a)
select * from cte_rn`
expected output:
update ParentExceptionId with ExceptionId of first record having same group id and keep ParentExceptionId of that first record null.
GroupId ExceptionId ParentExceptionId row
1 101 NULL 1
1 102 101 2
1 103 101 3
2 104 Null 1
2 105 104 2
2 106 104 3
3 107 NULL 1
3 108 107 2`
You can use first_value function :
select GroupId, ExceptionId,
(case when f_value <> ExceptionId then f_value end) as ParentExceptionId, row
from (select *, first_value(ExceptionId) over (partition by GroupId order by ExceptionId) f_value
from [dbo].[trn_Report6_Zone1_Exception] a
) a;
In same way you can use updateable cte :
with a as (
select *, first_value(ExceptionId) over (partition by GroupId order by ExceptionId) f_value
from [dbo].[trn_Report6_Zone1_Exception] a
)
update a
set ParentExceptionId = f_value
where f_value <> ExceptionId;
Try like this
SELECT * INTO #TAB FROM
(select 1,101,NULL,1 UNION ALL
select 1,102,NULL,2 UNION ALL
select 1,103,NULL,3 UNION ALL
select 2,104,NULL,1 UNION ALL
select 2,105,NULL,2 UNION ALL
select 2,106,NULL,3 UNION ALL
select 3,107,NULL,1 UNION ALL
select 3,108,NULL,2
)AS TABLEA(GroupId,ExceptionId,ParentExceptionId,rowW)
;WITH CTE AS(
SELECT GroupId, MIN(ExceptionId) MIN_ExceptionId
FROM #TAB
GROUP BY GroupId
)
UPDATE T SET T.ParentExceptionId = C.MIN_ExceptionId
FROM #TAB T
INNER JOIN CTE C ON T.GroupId = C.GroupId
WHERE rowW <>1

To 'flag' a specific condition in SQL query

I have a result set in the below format and I need to flag "GroupColumn"
-------------------------------------------------------------------
ID GroupColumn ConditionCol1 ConditionCol2
-------------------------------------------------------------------
1 101 ABC 99
2 101 DEF 99
3 102 ABC 01
4 102 DEF 01
5 103 ABC 02
6 103 DEF 99
7 104 DEF 02
8 104 DEF 99
First of the I need to flag the data based on "GroupColumn", with in this "GroupColumn" I am looking to satisfy Condition of "ABC" from one row and "99" from another row but not necessarily from the same row.
I looking to get a final result set some thing like this for the "Output" column
-------------------------------------------------------------------
ID GroupColumn ConditionCol1 ConditionCol2 Output
-------------------------------------------------------------------
1 101 ABC 99 Satisfied
2 101 DEF 99 Satisfied
3 102 ABC 01
4 102 DEF 01
5 103 ABC 02 Satisfied
6 103 DEF 99 Satisfied
7 104 DEF 02
8 104 DEF 99
You can do this using window functions:
select t.*,
(case when sum(case when conditioncol1 = 'ABC' then 1 else 0 end) over (partition by groupcolumn) > 0 and
sum(case when conditioncol2 = 99 then 1 else 0 end) over (partition by groupcolumn) > 0
then 'Satisfied'
end) as flag
from t;
An alternative is to use group by:
select t.*, tt.flag
from t join
(select groupcolumn, 'Satisfied' as flag
from t
where conditioncol1 = 'ABC' or conditioncol2 = 99
group by groupcolumn
having sum(case when conditioncol1 = 'ABC' then 1 else 0 end) > 0 and
sum(case when conditioncol2 = 99 then 1 else 0 end) > 0
) tt
on tt.groupcolumn = t.groupcolumn;
Assuming you are using SQL Server and you need to add Output column to your original table, you can try the following:
create table #temp
(GroupColumn int,ConditionCol1 varchar(20),ConditionCol2 int)
insert into #temp values (100,'ABC',99)
insert into #temp values (100,'DEF',99)
insert into #temp values (101,'ABC',02)
insert into #temp values (101,'DEF',99)
insert into #temp values (102,'DEF',99)
insert into #temp values (102,'DEF',99)
ALTER TABLE #temp
ADD [Output] varchar(10)
GO
;with cte(GroupColumn) as (
select GroupColumn
from #temp
where ConditionCol1 <> 'ABC'
and ConditionCol2 = 99
)
UPDATE t
SET [Output] = 'Satisfied'
FROM #temp t
INNER JOIN cte on t.GroupColumn = cte.GroupColumn
WHERE t.ConditionCol1 = 'ABC'
UPDATE t
SET [Output] = 'Satisfied'
FROM #temp t
WHERE [Output] is null
and t.GroupColumn in (Select GroupColumn from #temp where [Output]='Satisfied')
select * from #temp

Arithmetic operation on row value

I have a table with the below data
Tid Did value
------------------
1 123 100
1 234 200
2 123 323
2 234 233
All tids have dids as 123 and 234. So for every tid having dids 123 and 234 I want to calculate value of did 123/value of did 234 * 100 i.e 100/200 * 100
For tid 2 it will be value of did 123/value of did 234 * 100 i.e 323/233 * 100
The output table will be
Tid result
------------------
1 100/200 * 100
2 323/233 * 100
Any help?
JOIN the "123" rows with the "234" rows:
select t123.tid, t123.value * 100 / t234.value
from
(select tid, value from tablename where value = 123) t123
join
(select tid, value from tablename where value = 234) t234
on t123.tid = t234.tid
JOIN, all in ON
select t123.tid, t123.value * 100 / t234.value
from tablename t123
join tablename t234 on t123.tid = t234.tid and t123.did = 123 and t234.did = 234
Here is the query. We can use inner join to achieve it.
SELECT T1.Tid,(T1.value/T2.value)*100 AS Result
FROM Table_1 AS T1
INNER JOIN
Table_1 AS T2
ON (T1.Tid = T2.Tid)
AND (T1.Did <> T2.Did)
AND T1.Did = 123
select tid,
100 * sum(case when did = 123 then value end) /
sum(case when did = 234 then value end)
from your_table
group by tid
having sum(case when did = 234 then value end) > 0

Table Multiple Sql Query Union

I have following table which is having duplicate record with respect to different fields:
CREATE TABLE Student1
(`id` int,`status` int,`amount` int , `Name` varchar(10), `date` varchar(55))
;
INSERT INTO Student1
(`id`,`status`,`amount`, `Name`, `date`)
VALUES
(1,0,4500, 'ram', '04/02/2012'),
(2,0,2000, 'shyam', '05/09/2013'),
(4,0,1500, 'ghanshyam', '08/11/2014'),
(3,0,4500, 'gopal', '04/02/2012'),
(2,0,8000, 'radheshyam', '15/11/2013'),
(4,1,1500, 'ghanshyam', '18/10/2015'),
(1,1,4500, 'ram', '14/02/2012'),
(2,0,6500, 'radhe', '11/11/2014'),
(3,1,4500, 'gopal', '14/02/2015'),
(5,1,4500, 'gopala', '04/02/2015'),
(5,1,4500, 'gopala', '04/02/2015'),
(6,0,14500, 'gopal', '14/02/2015')
;
I have 3 conditions to filter the table:
No complete duplicate record
If record with same id but different Name and date field then add all of them to final result
If status=1 for any record then select that record of respective id
Add all record with id different from id of above 3 conditions
I have written this query:
SELECT * FROM Student1
GROUP BY id,status,amount,Name,date
HAVING COUNT(*) > 1
UNION
SELECT * FROM Student1 Student1
WHERE id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(DISTINCT name) > 1
AND COUNT(DISTINCT date) > 1
)
UNION
SELECT * FROM Student1 Student1
WHERE status=1 AND id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(id) >= 2
);
Result:
id status amount Name date
4 1 1500 ghanshyam 18/10/2015
1 1 4500 ram 14/02/2012
3 1 4500 gopal 14/02/2015
5 1 4500 gopala 04/02/2015
2 0 2000 shyam 05/09/2013
2 0 8000 radheshyam 15/11/2013
2 0 6500 radhe 11/11/2014
As you can see 1st Select avoid duplicate, 2nd Select record with same id having different Name, date, 3rd Select to get record with id=1. I am using UNION to avoid duplication in result.
Now I need to add 4th query to get the record id=6 which is not present in all the above condition.
Expected Result:
id status amount Name date
4 1 1500 ghanshyam 18/10/2015
1 1 4500 ram 14/02/2012
3 1 4500 gopal 14/02/2015
5 1 4500 gopala 04/02/2015
2 0 2000 shyam 05/09/2013
2 0 8000 radheshyam 15/11/2013
2 0 6500 radhe 11/11/2014
6 0 14500 gopal 14/02/2015
I need to solve the 4th query. Please help.
If the only condition you want to filter, is id = 6, it is enough to add just:
SELECT * FROM Student1 WHERE id=6;
Which is direct translation of your request into SQL. And full version will be:
SELECT * FROM Student1
GROUP BY id,status,amount,Name,date
HAVING COUNT(*) > 1
UNION
SELECT * FROM Student1 Student1
WHERE id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(DISTINCT name) > 1
AND COUNT(DISTINCT date) > 1
)
UNION
SELECT * FROM Student1 Student1
WHERE status=1 AND id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(id) >= 2
)
UNION
SELECT * FROM Student1 WHERE id=6;
Result is as requested but in different order. DEMO
However, if you would like to get all id which were not filtered with your coditions, but you don't know their values, I would use a VIEW:
CREATE VIEW Student2 AS
SELECT * FROM Student1
GROUP BY id,status,amount,Name,date
HAVING COUNT(*) > 1
UNION
SELECT * FROM Student1 Student1
WHERE id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(DISTINCT name) > 1
AND COUNT(DISTINCT date) > 1
)
UNION
SELECT * FROM Student1 Student1
WHERE status=1 AND id IN
(
SELECT id FROM Student1
GROUP BY id
HAVING COUNT(id) >= 2
);
And then query with it:
SELECT * FROM Student2
UNION
SELECT * FROM Student1 WHERE id NOT IN (SELECT id FROM Student2);
The result is the same. DEMO

SQL union same number of columns, same data types, different data

I have two result sets that look approximately like this:
Id Name Count
1 Asd 1
2 Sdf 4
3 Dfg 567
4 Fgh 23
But the Count column data is different for the second one and I would like both to be displayed, about like this:
Id Name Count from set 1 Count from set two
1 Asd 1 15
2 Sdf 4 840
3 Dfg 567 81
4 Fgh 23 9
How can I do this in SQL (with union if possible)?
My current SQL, hope this will better explain what I want to do:
(SELECT Id, Name, COUNT(*) FROM Customers where X)
union
(SELECT Id, Name, COUNT(*) FROM Customers where Y)
select *
from
(
SELECT 'S1' as dataset, Id, Name, COUNT(*) as resultcount FROM Customers where X
union
SELECT 'S2',Id, Name, COUNT(*) FROM Customers where Y
) s
pivot
(sum(resultcount) for dataset in (s1,s2)) p
You can do something like:
;WITH Unioned
AS
(
SELECT 'Set1' FromWhat, Id, Name FROM Table1
UNION ALL
SELECT 'Set2', Id, Name FROM Table2
)
SELECT
Id,
Name,
SUM(CASE FromWhat WHEN 'Set1' THEN 1 ELSE 0 END) 'Count from set 1',
SUM(CASE FromWhat WHEN 'Set2' THEN 1 ELSE 0 END) 'Count from set 2'
FROM Unioned
GROUP BY Id, Name;
SQL Fiddle Demo