Query for Multiple row SQL Where clause - sql

Need help for one query which is fetching result from multiple rows based on some condition. For e.g. we have table with [Roll no] with [subjects]. Table can have multiple records for the same [Roll No]. My requirement is if the Student opt for only 'English' then result should return 'E', if Maths then 'M' and if both then 'B'.

// I think this is what you want.
INSERT INTO dbo.rolls
( name, subject )
VALUES ( 'Jones', 'English'),
( 'Smith', 'Math'),
('Adams','English'),
('Adams', 'Math')
GO
;WITH CTE AS (
SELECT subquery1.name, 'B' AS code FROM (
SELECT name,COUNT(name) AS cnt
FROM rolls
WHERE subject = 'English' OR subject = 'Math'
GROUP BY name
HAVING COUNT(name) > 1 ) AS subquery1
UNION
SELECT subquery2.name, SUBSTRING(rolls.subject,1,1) AS code FROM (
SELECT name,COUNT(name) AS cnt
FROM rolls
WHERE subject = 'English' OR subject = 'Math'
GROUP BY name
HAVING COUNT(name) = 1 ) AS subquery2
INNER JOIN dbo.rolls
ON rolls.name = subquery2.name
)
SELECT * FROM CTE

Related

COUNT & PRINT matched Row values in NEW COLUMNS

I am trying to make the column name from row value and count the total value of each row name to show into the particular column name. Result will show from 30 days.
SELECT id
, Name
, Designation
, DeptName
, Sts
, COUNT(Sts) as 'COUNT'
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName, Sts
ORDER id
Here is my output
Required Output is...
use conditional aggregation
SELECT id
, Name
, Designation
, DeptName
, sum (case when Sts='H' then 1 end) H
,sum (case when Sts='A' then 1 end) A
,sum (case when Sts='P' then 1 end) P
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName
ORDER id
The CASE statement goes through conditions and return a value when the first condition is met (like an IF-THEN-ELSE statement) and when a condition will meet it will sum up for that particular value
As eariler suggested, use pivot
WITH temp AS(
SELECT 1 AS id,'Neo' AS name,'Zonal Manager' AS designation, 'Admin' AS deptname, 'H' AS sts ,2 AS cnt FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',3 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'P',4 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',1 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',8 FROM dual )
SELECT p.*
FROM temp
PIVOT(
SUM(cnt)
FOR sts
IN ('A', 'P', 'H')
) p

Combine rows if value is blank

I'm using SQL-Server 2008. I need to combine rows with the same Name and increase counter when:
1 or more Id's for the same Name is blank
NOT merge rows if Id is NULL!
NOT merge rows if have the same Name, but different Ids
Output for now:
Name Id Cnt
John 1 1
Peter 2 2 -- This Peter with the same Id have 2 entries so Cnt = 2
Peter 3 1 -- This is other Peter with 1 entry so Cnt = 1
Lisa 4 1
Lisa NULL 1
David 5 1
David 1 -- here Id is blank ''
Ralph 2 -- Ralph have both rows with blank Id so Cnt = 2
Desired output:
Name Id Cnt
John 1 1
Peter 2 2
Peter 3 1
Lisa 4 1
Lisa NULL 1 -- null still here
David 5 2 -- merged with blank '' so Cnt = 2
Ralph 2 -- merged both blanks '' so Cnt = 2
SQL-Query:
This is sample query what I'm using for now:
SELECT Name,
Id,
COUNT(Id) AS Cnt
FROM Employees
WHERE Condition = 1
GROUP BY Name, Id
What I have tried:
Added aggregate MAX to Id in SELECT clause and grouped by Name only, but in this case merged rows with NULL values and with the same names with different Id's what is wrong for me.
SELECT Name,
MAX(Id), -- added aggregate
COUNT(Id) AS Cnt
FROM Employees
WHERE Condition = 1
GROUP BY Name -- grouped by Name only
Have you any ideas? If anything is not clear about problem - ask me, I will provide more details.
UPDATE:
DDL
CREATE TABLE Employees
(
Name NVARCHAR(40),
Id NVARCHAR(40)
);
DML
INSERT INTO Employees VALUES
('John' , '1')
,('Peter', '2')
,('Peter', '2')
,('Peter', '3')
,('Lisa' , '4')
,('Lisa' , NULL)
,('David', '5')
,('David', '')
,('Ralph', '')
,('Ralph', '')
DEMO: SQL FIDDLE
Edit
DECLARE #Data table (Name varchar(10), Id varchar(10)) -- Id must be varchar for blank value
INSERT #Data VALUES
('John', '1'),
('Peter', '2'),('Peter', '2'),
('Peter', '3'),--('Peter', ''), --For test
('Lisa', '4'),
('Lisa', NULL),
('David', '5'),
('David', ''),
('Ralph', ''), ('Ralph', '')
SELECT
Name,
Id,
COUNT(*) + ISNULL(
(SELECT COUNT(*) FROM #data WHERE Name = d.Name AND Id = '' AND d.Id <> '')
, 0) AS Cnt
FROM #data d
WHERE
Id IS NULL
OR Id <> ''
OR NOT EXISTS(SELECT * FROM #data WHERE Name = d.Name AND Id <> '')
GROUP BY Name, Id
You can use CASE statement inside your SELECT. It allows you to set Id = [some value] for employees where it is blank. Query can be something like this:
SELECT E.Name,
CASE
WHEN E.Id = ''
THEN
(Select Employees.Id from Employees where Employees.Id <> '' and E.Name = Employees.Name)
ELSE E.Id
END as Idx,
COUNT(Id) AS Cnt
FROM Employees as E
WHERE Condition = 1
GROUP BY Name, Idx
A version with window functions:
SELECT Name,ID, Cnt from
( select *, sum(1-AmtBlank) over (partition by Name, ID) + sum(case id when 0 then 1 else 0 end) over (partition by Name) Cnt,
rank() over (partition by Name order by AmtBlank ) rnk,
row_number() over (partition by Name, ID order by AmtBlank) rnr
FROM (select * , case id when '' then 1 else 0 end AmtBlank from Employees /*WHERE Condition = 1*/ ) e
) c where rnr=1 and rnk = 1
This uses case id when '' then 1 else 0 end AmtBlank to keep an amount for the blank amounts per row (making the amount for non blanks 1-AmtBlank) and 2 window functions, one with id for a count per name and id (sum(1-AmtBlank) over (partition by Name, ID)) and a count for all blanks in a name section (sum(case id when 0 then 1 else 0 end) over (partition by Name))
The row_number is used to subsequently fetch only the first rows of a group and rank is used to only include the blank records when there are no records with an id.
Try this. using cte and joins
;with cte as (
SELECT Name,
Id,
COUNT(*) AS Cnt
FROM Employees
WHERE isnull(Id,1)<>''
GROUP BY Name, Id
),
cte2 as (SELECT Name,id, COUNT(*) AS Cnt FROM Employees WHERE Id='' GROUP BY Name,id)
select cte.Name,cte.Id,(cte.cnt + ISNULL(cte2.Cnt,0)) as cnt
from cte
left JOIN cte2
on cte.Name = cte2.Name
union all
select cte2.Name,cte2.Id,cte2.cnt
from cte2
left JOIN cte
on cte.Name = cte2.Name
where cte.Name is null
You could try something like this.
;WITH NonBlanks AS
(
SELECT Name,
Id,
COUNT(ISNULL(Id, 1)) AS Cnt
FROM Employees
WHERE ISNULL(Id,0) <> ''
GROUP BY Name, Id
)
,Blanks AS
(
SELECT Name,
Id,
COUNT(ISNULL(Id, 1)) AS Cnt
FROM Employees
WHERE ID = ''
GROUP BY Name, Id
)
SELECT CASE WHEN nb.NAME IS NULL THEN b.NAME ELSE nb.NAME END NAME,
CASE WHEN nb.NAME IS NULL THEN b.Id ELSE nb.Id END Id,
(ISNULL(nb.Cnt,0) + ISNULL(b.Cnt,0)) Cnt
FROM NonBlanks nb FULL JOIN Blanks b
ON nb.Name = b.Name
This simple syntax is compatible with older versions or other RDBMSs
-- Self explained on comments
edited:
select name, id, count(*) from (
-- adds "normal" records
select name, id from Employees where id is null or id <> ''
-- adds one record to each name-notBlankId for each blank id (David, Peter if you add 'Peter','')
-- uncomment /*or id is null*/ if you want even null ids to recieve merged blanks
union all
select e1.name, e1.id
from (select distinct name, id from Employees where id <> '' /*or id is null*/ ) as e1
inner join (select name, id from Employees where id = '') as e2 on e1.name = e2.name
-- adds records that can't be merged (Ralph)
union all
select name, id from Employees e1
where e1.id = ''
and not exists(select * from Employees e2 where e1.name = e2.name and e2.id <> '')
) as fullrecords
group by name, id

SQL - select students that had exams on every subject

I have two tables:
Exams(StudentID, SubjectID),
Subjects(SubjectID)
And want to select students that had exam on all subjects. How to do it?
Is it possible without using GROUP and COUNT?
You can do it in many ways. One of those:
DECLARE #students TABLE ( id INT )
DECLARE #exams TABLE ( id INT )
DECLARE #studentexams TABLE
(
studentid INT ,
examid INT
)
INSERT INTO #exams
VALUES ( 1 ),
( 2 )
INSERT INTO #students
VALUES ( 1 ),
( 2 ),
( 3 )
INSERT INTO #studentexams
VALUES ( 1, 1 ),
( 1, 2 ),
( 2, 1 )
SELECT *
FROM #students s
WHERE NOT EXISTS ( SELECT *
FROM #exams e
WHERE e.id NOT IN ( SELECT se.examid
FROM #studentexams se
WHERE se.studentid = s.id ) )
Output:
id
1
Is this what you're after:
--list all students
select *
from #students st
--where there isn't
where not exists
(
--any subject
select top 1 1
from #subjects su
--for which that student did not take an exam
where su.id not in
(
select subjectid
from #exams e
where studentId = st.id
)
)
Here's the full code (i.e. including sample data tables):
declare #subjects table(id bigint not null identity(1,1), title nvarchar(32))
declare #students table(id bigint not null identity(1,1), name nvarchar(32))
declare #exams table(id bigint not null identity(1,1), studentId bigint, subjectId bigint, grade nchar(1), attempt int)
insert #subjects select 'Maths' union select 'English' union select 'Geography' union select 'Computer Science'
insert #students select 'Anna' union select 'Billy' union select 'Christie' union select 'Daniel'
insert #exams select st.Id, su.Id, grade, attempt
from
(
select 'Anna' student, 'Maths' subject, 'A' grade, 1 attempt
union select 'Anna' student, 'English' subject, 'A' grade, 1 attempt
union select 'Anna' student, 'Geography' subject, 'A' grade, 1 attempt
union select 'Anna' student, 'Computer Science' subject, 'A' grade, 1 attempt
union select 'Billy' student, 'Maths' subject, 'A' grade, 1 attempt
union select 'Billy' student, 'Computer Science' subject, 'A' grade, 1 attempt
union select 'Christie' student, 'Maths' subject, 'A' grade, 1 attempt
union select 'Christie' student, 'English' subject, 'F' grade, 1 attempt
union select 'Christie' student, 'English' subject, 'E' grade, 2 attempt
union select 'Christie' student, 'English' subject, 'A' grade, 3 attempt
union select 'Daniel' student, 'Maths' subject, 'A' grade, 1 attempt
union select 'Daniel' student, 'English' subject, 'A' grade, 1 attempt
union select 'Daniel' student, 'Geography' subject, 'A' grade, 1 attempt
union select 'Daniel' student, 'Computer Science' subject, 'F' grade, 1 attempt
union select 'Daniel' student, 'Computer Science' subject, 'A' grade, 2 attempt
) x
inner join #students st on st.name = x.student
inner join #subjects su on su.title = x.subject
--list all students
select *
from #students st
--where there isn't
where not exists
(
--any subject
select top 1 1
from #subjects su
--for which that student did not take an exam
where su.id not in
(
select subjectid
from #exams e
where studentId = st.id
)
)

Select literals as table data in SQL server

Consider table Address , with fields Country, State, and other data fields. I want to get all the records except for those with Country,State combination as (US, IL), (US,LA), (IND,DEL)
The query goes like
Select * from Address a
where not exists
(
select Country,State
(select 'US' as Country, 'IL' as State
union
select 'US' as Country, 'LA' as State
union
select 'IND' as Country, 'DEL' as State
) e
where e.Country != a.Country and e.State != a.state
)
How can it be easily achieved (to replace coutry,state combination of union with simple subquery)? As total data is not very large, i am least bothered about performance for now.
I know i can create table variable, add all literal combination there using insert into syntax, and use table variable for not exists, but i feel it is overkill for small requirement (not exists on 2 variables).
Looks like your query tried to do this:
select *
from Address a
where not exists (
select *
from (
select 'US' as Country, 'IL' as State union all
select 'US' as Country, 'LA' as State union all
select 'IND' as Country, 'DEL' as State
) e
where e.Country = a.Country and
e.State = a.State
)
Or you could not use a derived table and still get the same result
select *
from Address as a
where not (
a.Country = 'US' and a.State = 'IL' or
a.Country = 'US' and a.State = 'LA' or
a.Country = 'IND' and a.State = 'DEL'
)
Simply use the values directly in the query:
-- Sample data.
declare #Table as Table ( Country VarChar(6), State VarChar(6), Foo VarChar(6) );
insert into #Table ( Country, State, Foo ) values
( 'US', 'IL', 'one' ), ( 'XX', 'LA', 'two' ), ( 'IND', 'XXX', 'three' ), ( 'IND', 'DEL', 'four' );
select * from #Table;
-- Demonstrate excluding specific combinations.
select T.*
from #Table as T left outer join
( values ( 'US', 'IL' ), ( 'US', 'LA' ), ( 'IND', 'DEL' ) ) as Exclude( Country, State )
on T.Country = Exclude.Country and T.State = Exclude.State
where Exclude.Country is NULL;
or
select *
from Address a
left outer join
( select 'US' as Country, 'IL' as State
union select 'US', 'LA'
union select 'IND', 'DEL' ) as n
on a.Country = n.Country and a.State = n.State
where n.Country is NULL;

CASE condition and how to code it?

I am creating a column in a new table( Table B) called Number of different locations. This column is derived from two columns in a Table A - Customer and Location.
Sample Data from Table A .
Customer Location
Mr James Smith Los Angeles
Mr David Jones London
Mr James Smith Paris
So the pseudo code ?
[Number of Different Locations] =
CASE
When Customer has more than one location ( count greater than 1 of for distinct customer)
Then populate those entries as 'Y'
Else 'N'
Now I have tried a few ways to code the 1st condition but it does not work .
CASE
When EXISTS ( select distinct customer, count ( Location ) from Table B
group by customer)
then 'Y'
Else 'N'
What am I doing wrong ? All the values are coming out in resultant table as 'Y'
SELECT Customer, Location, [Number of different locations] =
CASE
When EXISTS ( select distinct customer, count ( Location ) from Table B
group by customer
having count(location)>1)
then 'Y'
Else 'N'
END
FROM [Table]
you didn't specify "Having > 1"
You don't really need a separate subselect for that. This can be handled entirely within a GROUP BY clause and using the count of locations to determine if there are multiple locations.
SELECT Customer
, MultipleLocations =
CASE WHEN COUNT(Location) > 1
THEN 'Y'
ELSE 'N'
END
FROM YourTable
GROUP BY
Customer
Should your table contain multiple records for a customer with the same location, you can add a DISTINCT clause to accomodate for this.
SELECT Customer
, MultipleLocations =
CASE WHEN COUNT(DISTINCT Location) > 1
THEN 'Y'
ELSE 'N'
END
FROM YourTable
GROUP BY
Customer
Maybe this will help did it in another way:
DECLARE #tbl TABLE
(
Customer VARCHAR(100),
Location VARCHAR(100)
)
INSERT INTO #tbl
SELECT 'Mr James Smith','Los Angeles'
UNION ALL
SELECT 'Mr David Jones','London'
UNION ALL
SELECT 'Mr James Smith','Paris'
;WITH CTE AS
(
SELECT
COUNT(*) OVER(PARTITION BY tbl.Customer) AS NbrOf,
tbl.Customer,
tbl.Location
FROM
#tbl AS tbl
)
SELECT
CTE.Customer,
CTE.Location,
(
CASE
WHEN CTE.NbrOf>1
THEN 'Y'
ELSE 'N'
END
) AS newColumn
FROM
CTE
WITH TableA
AS
(
SELECT *
FROM (
VALUES ('Mr James Smith', 'Los Angeles'),
('Mr David Jones', 'London'),
('Mr James Smith', 'Paris')
) AS T (Customer, Location)
),
TableACustomerTallies
AS
(
SELECT Customer, COUNT(DISTINCT Location) AS Tally
FROM TableA
GROUP
BY Customer
)
SELECT Customer,
'Y' AS HasMultipleLocations
FROM TableACustomerTallies
WHERE Tally > 1
UNION
SELECT Customer,
'N' AS HasMultipleLocations
FROM TableACustomerTallies
WHERE Tally <= 1;