Calculate Count from a view ( SQL) - sql

I have this View :
SELECT [ID]
,[PersonName]
,[PersonFUNCTION]
,[GUESTName]
,[Team]
,[sector]
,[MeetingCity]
,[GUESTCOMPANY]
,[TypeMeeting]
FROM [DB_TEST].[dbo].[Meetings]
From this view we can Read for Exemple :
The Person with the Name "XXX" (PersonName) who is A CEO (PersonFUNCTION) was in a meeting with "Mark Zuckerberg" (GUESTName) in Paris ( MeetingCity ) and "Facebook" is the ( GUESTCOMPANY ) and finally the meeting was a "One to One Meeting" (TypeMeeting) !
PS : Note that XXX can meet Mark Zuckerberg more than one time , in a different city for example.
What I Want to do is :
Add 3 columns : Count( One to One Meeting ) and Count( One to Few Meeting ) and Count ( Group Meeting )
Count( One to One Meeting ) = how many times the [PersonName] has met the [GUESTName] in a One to One meeting no matter if the city is different or anything else is different ...
So something like that :
SELECT [ID]
,[PersonName]
,[PersonFUNCTION]
,Count( One to One Meeting between PersonName and GUESTName ) ?
,Count( One to Few Meeting between PersonName and GUESTName) ?
,Count ( Group Meeting between PersonName and GUESTName) ?
,[GUESTName]
,[Team]
,[sector]
,[MeetingCity]
,[GUESTCOMPANY]
,[TypeMeeting]
FROM [DB_TEST].[dbo].[Meetings]
Thanks

Something like this should help you get all columns and counts.
SELECT [ID],
[PersonName],
[PersonFUNCTION],
m2.OneToOneCount, --Count( One to One Meeting between PersonName and GUESTName )
m2.OneToFewCount, --Count( One to Few Meeting between PersonName and GUESTName)
m2.GroupCount, --Count ( Group Meeting between PersonName and GUESTName)
[GUESTName],
[Team],
[sector],
[MeetingCity],
[GUESTCOMPANY],
[TypeMeeting]
FROM [DB_TEST].[dbo].[Meetings] m
CROSS APPLY (SELECT COUNT(CASE WHEN m2.[TypeMeeting] = 'OneToOne' THEN 1 END) AS OneToOneCount,
COUNT(CASE WHEN m2.[TypeMeeting] = 'OneToFew' THEN 1 END) AS OneToFewCount,
COUNT(CASE WHEN m2.[TypeMeeting] = 'Group' THEN 1 END) AS GroupCount
FROM [DB_TEST].[dbo].[Meetings] m2
WHERE m2.[PersonName] = m.[PersonName]
AND m2.[GUESTName] = m.[GUESTName]) m2
if you can't use CROSS APPLY, this is a JOIN alternative.
SELECT [ID],
[PersonName],
[PersonFUNCTION],
m2.OneToOneCount, --Count( One to One Meeting between PersonName and GUESTName )
m2.OneToFewCount, --Count( One to Few Meeting between PersonName and GUESTName)
m2.GroupCount, --Count ( Group Meeting between PersonName and GUESTName)
[GUESTName],
[Team],
[sector],
[MeetingCity],
[GUESTCOMPANY],
[TypeMeeting]
FROM [DB_TEST].[dbo].[Meetings] m
JOIN ( SELECT [PersonName],
[GUESTName],
COUNT(CASE WHEN m2.[TypeMeeting] = 'OneToOne' THEN 1 END) AS OneToOneCount,
COUNT(CASE WHEN m2.[TypeMeeting] = 'OneToFew' THEN 1 END) AS OneToFewCount,
COUNT(CASE WHEN m2.[TypeMeeting] = 'Group' THEN 1 END) AS GroupCount
FROM [DB_TEST].[dbo].[Meetings] m2
GROUP BY [PersonName],
[GUESTName]
) m2 ON m2.[PersonName] = m.[PersonName]
AND m2.[GUESTName] = m.[GUESTName]

BM
Another solution might be using SQL Pivot query
Here is sample data and SQL pivot Select statement
SELECT *
FROM (
SELECT
[ID],
[PersonName],
[TypeMeeting]
FROM [Meetings]
) TableData
PIVOT (
Count(ID)
FOR [TypeMeeting] IN (
[T1],[T2],[T3],[T4],[T5],[T6]
)
) PivotTable
I just used 'T1', etc for meeting types, you are required to replace them with actual values within "[]"
Here is the result
If you have many different meeting types you can use dynamic pivot query in SQL Server but I guess above solution will be enough

You can use conditional aggregation:
select PersonName, GuestName,
sum(case when TypeMetting = 'one-to-one' then 1 else 0 end) as Num_OneToOne,
sum(case when TypeMetting = 'one-to-few' then 1 else 0 end) as Num_OneToFew,
sum(case when TypeMetting = 'group' then 1 else 0 end) as Num_Group
from Meetings
group by PersonName, GuestName;

You can use SQL COUNT function with Partition By clause
Try
SELECT Distinct [ID]
,[PersonName]
,[TypeMeeting] --...
,COUNT([TypeMeeting]) OVER (PARTITION BY [PersonName], [TypeMeeting]) Cnt
FROM [Meetings]
It will result as follows

Related

How to calculate Total records Counts in table with my scenario

I am Writing a SQL Code which basically count stars values Like ratings Feedback forms.To calculate how many peoples rate
1 star,2,3 and so on i am using pivot unpivot SQL properties to calculate count against of each number hits.Now i want to calculate count of all records in the table along with my this query.
i want to achieve my desire result without sub-query.
WITH survey AS (
SELECT
*
FROM
(
SELECT
student_id,
performance,
teacher_behaviour,
survey_id
FROM
survey_feedback
WHERE
survey_id = 1
GROUP BY
student_id,
performance,
teacher_behaviour,
survey_id
) UNPIVOT ( star
FOR q
IN ( performance AS 'PERFORMANCE',
teacher_behaviour AS 'TEACHER_BEHAVIOUR'
) )
ORDER BY
1,
2
)
SELECT
*
FROM
survey PIVOT (
COUNT ( student_id )
FOR star
IN ( 1, 2, 3, 4, 5 )
)
ORDER BY
q;
enter image description here
Hmmm . . . I'm not a fan of PIVOT and UNPIVOT. They really offer no new functionality and are quite bespoke and not powerful enough.
So, I would just unpivot and use conditional aggregation:
SELECT survey_id, which, COUNT(rating) as total,
SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) as rating_1,
SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) as rating_2,
SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) as rating_3,
SUM(CASE WHEN rating = 4 THEN 1 ELSE 0 END) as rating_4,
SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) as rating_5
FROM (SELECT sf.*,
(CASE WHEN which = 'PERFORMANCE' THEN performance
WHEN which = 'TEACHER_BEHAVIOUR' THEN teacher_behavior
END) as rating
FROM survey_feedback sf CROSS JOIN
(SELECT 'PERFORMANCE' as which FROM DUAL UNION ALL
SELECT 'TEACHER_BEHAVIOUR' FROM DUAL
) n
) sf
WHERE sf.survey_id = 1
GROUP BY sf.survey_id, which;
In Oracle 12C+, I would use a lateral join for unpivoting.

How to get count of items present in each category but not present in other categories?

I have a table with different visit_types to hospital. They are Inpatient, Outpatient, Emergency
I would like to know the count of subjects solely present under each visit_type but not in other visit_types. In the above example the
Inpatient count - 4
Outpatient count -2
Emergency count - 3
I tried the below but not sure whether it is accurate?
SELECT count(DISTINCT PERSON_ID) FROM Visit WHERE PERSON_ID NOT IN
(select distinct person_id from Visit where visit_type = 'Inpatient')
AND VISIT_type = 'Outpatient';
SELECT count(DISTINCT PERSON_ID) FROM Visit WHERE PERSON_ID NOT IN
(select distinct person_id from Visit where visit_type = 'Inpatient')
AND VISIT_type = 'Emergency';
When I do this, it includes common subjects between Emergency and Outpatient?
How can I get the count correctly?
With a CTE which returns for each person_id all the types:
with cte as (
select person_id,
sum(case visit_type when 'Inpatient' then 1 else 0 end) Inpatient,
sum(case visit_type when 'Outpatient' then 1 else 0 end) Outpatient,
sum(case visit_type when 'Emergency' then 1 else 0 end) Emergency
from Visit
group by person_id
)
select
case
when Inpatient > 0 then 'Inpatient'
when Outpatient > 0 then 'Outpatient'
when Emergency > 0 then 'Emergency'
end visit_type,
count(*) counter
from cte
group by visit_type
See the demo.
Results:
visit_type | counter
:--------- | ------:
Outpatient | 2
Emergency | 3
Inpatient | 4
I would like to know the count of subjects solely present under each category but not in other categories.
You can aggregate by patient, keeping track of the categories. Then aggregate again:
select visit_type, count(*)
from (select patientId, min(visit_type) as visit_type
from t
group by patientId
having min(visit_type) = max(visit_type)
) p
group by visit_type;
An alternative method uses group by but filters before aggregation:
select visit_type, count(*)
from t
where not exists (select 1
from t t2
where t2.patientid = t.patientid and
t2.visit_type <> t.visit_type
)
group by visit_type;
Note: In this case, the count(*) is counting rows. If your data has duplicates, use count(distinct visit_type).
I have no idea what "I consider Inpatient category as base category" is supposed to mean, but the question itself is quite clear.
EDIT:
I am unclear on the relationships between the different categories that you want. You may find it most flexible to use:
select visit_type, count(*)
from (select patientId,
bool_or(visit_type = 'Inpatient') as has_inpatient,
bool_or(visit_type = 'Outpatient') as has_oupatient,
bool_or(visit_type = 'Emergency') as has_emergency,
count(distinct visit_type) as num_visit_types
from t
group by patientId
) p
where num_visit_types = 1
group by visit_type;
This version is the same as the earlier two queries. But you can use the has_ flags for additional filtering -- for instance where num_visit_types = 1 or (num_visit_types = 2 and has_inpatient) if you want people with one type or one type plus "inpatient".
You can use this query!
SELECT
C.visit_type,
COUNT(*) AS count_per_visit_type
FROM (
SELECT
person_id
FROM (
SELECT
person_id,
ARRAY_AGG(DISTINCT visit_type) AS visit_type_array
FROM visit
GROUP BY person_id
) A
WHERE LENGTH(visit_type_array) = 1
) B
JOIN visit C
ON B.person_id = C.person_id
GROUP BY C.visit_type

Combine values from multiple rows into one row with each value in it's own column

I have a number of sites were customers can register and I want to combine in one row all the customer IDs associated with an email address across all sites. I am working in SQL Sever.
Example source data:
What I would like to achieve in a new table:
My first thought was something like this, with email as primary key but it doesn't work. Could I use a CASE expression?
SELECT c.[Email],
(SELECT c.[CustomerID] FROM Customer c WHERE c.[BrandID] = '1859') AS [Brand1],
(SELECT c.[CustomerID] FROM Customer c WHERE c.[BrandID] = '1594') AS [Brand2],
(SELECT c.[CustomerID] FROM Customer c WHERE c.[BrandID] = '4578') AS [Brand3]
FROM Customer c
Just use conditional aggregation:
SELECT c.Email,
MAX(CASE WHEN BrandID = 1859 THEN c.[CustomerID] END) as Brand1,
MAX(CASE WHEN BrandID = 1594 THEN c.[CustomerID] END) as Brand2,
MAX(CASE WHEN BrandID = 4578 THEN c.[CustomerID] END) as Brand3
FROM Customer c
GROUP BY c.Email;
BrandID looks like a number so I removed the single quotes around the comparison value. Of course, use single quotes if it is really a string.
You can try case like this:
SELECT c.Email,
SUM(CASE WHEN BrandID = 1859 THEN CustomerID ELSE 0 END) as Brand1,
SUM(CASE WHEN BrandID = 1594 THEN CustomerID ELSE 0 END) as Brand2,
SUM(CASE WHEN BrandID = 4578 THEN CustomerID ELSE 0 END) as Brand3
FROM Customer c
GROUP BY c.Email;
You may use pivot for same.
; with cte as (
select row_number() over (partition by email order by brandid) as Slno,
Email, brandid from table)
, ct as (
select brandid, 'brand' + cast(slno as varchar(5)) as Col, email from cte )
select * from (
select brandid, col, email from ct ) as d
pivot (
max(brandid) for col in ( [brand1], [brand2], [brand3] )
) as p

How to get first and last record in HiveSQL if key is different

I need to get the first and last record for a user if one of the key fields is different over time using a Hive table:
This is some sample data:
UserID EntryDate Activity
a3324 1/1/16 walk
a3324 1/2/16 walk
a3324 1/3/16 walk
a3324 1/4/16 run
a5613 1/1/16 walk
a5613 1/2/16 walk
a5613 1/3/16 walk
a5613 1/4/16 walk
And I'm looking for output preferably like this:
a3324 1/1/16 walk 1/4/16 run
Or at least like this:
a3324 walk run
I start writing code like this:
SELECT UserID, MINIMUM(EntryDate), MAXIMUM(EntryDate), Activity
FROM
SELECT UserID, DISTINCT Activity
GROUP BY UserID
HAVING Count(Activity) > 1
But I know that's not it.
I'd also like to be able to specify the cases where the original activity was Walk and the second activity was Run perhaps in the Where clause.
Can you help with an approach?
Thanks
You can use lag /lead to get a solution
SELECT * FROM (
select UserID ,EntryDate , Activityslec,
lead(Activityslec, 1) over (UserID ,EntryDate ) as nextActivityslec
from table) as A
where Activityslec <> nextActivityslec
SELECT
t.UserId
,MIN(CASE WHEN t.RowNumAsc = 1 THEN t.EntryDate END) as MinEntryDate
,MIN(CASE WHEN t.RowNumAsc = 1 THEN t.Activity END) as MinActivity
,MAX(CASE WHEN t.RowNumDesc = 1 THEN t.EntryDate END) as MaxEntryDate
,MAX(CASE WHEN t.RowNumDesc = 1 THEN t.Activity END) as MaxActivity
FROM
(
SELECT
UserId
,EntryDate
,Activity
,ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY EntryDate) as RowNumAsc
,ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY EntryDate DESC) as RowNumDesc
FROM
Table
) t
WHERE
t.RowNumAsc = 1
OR t.RowNumDesc = 1
GROUP BY
t.UserId
Looks like window functions are supported (https://cwiki.apache.org/confluence/display/Hive/LanguageManual+WindowingAndAnalytics) so using 2 row numbers 1 for EntryDate Ascending and another for Descending with Conditional Aggregation should get you to the answer.
And if you don't want to use Analytic Functions (window functions) you can use self left joins and conditional aggregation:
SELECT
t.UserId
,MIN(CASE WHEN mn.UserId IS NULL THEN t.EntryDate END) as MinEntryDate
,MIN(CASE WHEN mn.UserId IS NULL THEN t.Activity END) as MinActivity
,MAX(CASE WHEN mx.UserId IS NULL THEN t.EntryDate END) as MaxEntryDate
,MAX(CASE WHEN mx.UserId IS NULL THEN t.Activity END) as MaxActivity
FROM
Table t
LEFT JOIN Table mn
ON t.UserId = mn.UserId
AND t.EntryDate > mn.EntryDate
LEFT JOIN Table mx
ON t.UserId = mx.UserId
AND t.EntryDate < mx.EntryDate
WHERE
mn.UserId IS NULL
OR mx.UserId IS NULL
GROUP BY
t.UserId
Or a correlated Sub Query way:
SELECT
UserId
,MIN(EntryDate) as MinEntryDate
,(SELECT
Activity
FROM
Activity a
WHERE
u.UserId = a.UserId
AND a.EntryDate = MIN(u.EntryDate)
LIMIT 1
) as MinActivity
,MAX(EntryDate) as MaxEntryDate
,(SELECT
Activity
FROM
Activity a
WHERE
u.UserId = a.UserId
AND a.EntryDate = MAX(u.EntryDate)
LIMIT 1
) as MaxActivity
FROM
Activity u
GROUP BY
UserId

SQL Aggreate Functions

I have table which list a number of cases and assigned primary and secondary technicians. What I am trying to accomplish is to aggregate the number of cases a technician has worked as a primary and secondary tech. Should look something like this...
Technician Primary Secondary
John 4 3
Stacy 3 1
Michael 5 3
The table that I am pulling that data from looks like this:
CaseID, PrimaryTech, SecondaryTech, DOS
In the past I have used something like this, but now my superiors are asking for the number of secondary cases as well...
SELECT PrimaryTech, COUNT(CaseID) as Total
GROUP BY PrimaryTech
I've done a bit of searching, but cant seem to find the answer to my problem.
Select Tech,
sum(case when IsPrimary = 1 then 1 else 0 end) as PrimaryCount,
sum(case when IsPrimary = 0 then 1 else 0 end) as SecondaryCount
from
(
SELECT SecondaryTech as Tech, 0 as IsPrimary
FROM your_table
union all
SELECT PrimaryTech as Tech, 1 as IsPrimary
FROM your_table
) x
GROUP BY Tech
You can group two subqueries together with a FULL JOIN as demonstrated in this SQLFiddle.
SELECT Technician = COALESCE(pri.Technician, sec.Technician)
, PrimaryTech
, SecondaryTech
FROM
(SELECT Technician = PrimaryTech
, PrimaryTech = COUNT(*)
FROM Cases
WHERE PrimaryTech IS NOT NULL
GROUP BY PrimaryTech) pri
FULL JOIN
(SELECT Technician = SecondaryTech
, SecondaryTech = COUNT(*)
FROM Cases
WHERE SecondaryTech IS NOT NULL
GROUP BY SecondaryTech) sec
ON pri.Technician = sec.Technician
ORDER By Technician;
SELECT COALESCE(A.NAME, B.NAME) AS NAME, CASE WHEN A.CASES IS NOT NULL THEN A.CASES ELSE 0 END AS PRIMARY_CASES,
CASE WHEN B.CASES IS NOT NULL THEN B.CASES ELSE 0 END AS SECONDARY_CASES
FROM
(
SELECT COUNT(*) AS CASES, PRIMARYTECH AS NAME FROM YOUR_TABLE
GROUP BY PRIMARYTECH
) AS A
FULL OUTER JOIN
(
SELECT COUNT(*) AS CASES, SECONDARYTECH AS NAME FROM YOUR_TABLE
GROUP BY SECONDARYTECH
) AS B
ON A.NAME = B.NAME