I want to display row values as column values. and also display the final total value at end of the table.
To do that I'm using the below data set.
I want to set this data in the column side.
I used this SQL query to do that. But I don't know how to get Hours Total Column
select *
from
(select EMP_NO,SUM(Hours) total
from Employee_Attendence
group by EMP_NO)
pivot
(sum(total)
for WAGE_Type in ('Absence', 'Normal'))
Final output should display as below.
Select EMP_NO, Absence, Normal, Total
From
(select *
from
(select EMP_NO, sum(Hours) total
from Employee_Attendence
group by EMP_NO)
pivot
(sum(total)
for WAGE_Type in ('Absence', 'Normal'))
)
A simple conditional aggregation should do the trick
Example
Select Emp_ID
,Absence = sum(case when Wage_Type ='Absence' then Hours else 0 end)
,Normal = sum(case when Wage_Type ='Normal' then Hours else 0 end)
,Total = sum(Hours)
From YourTable
Group By Emp_ID
Results
Emp_ID Absence Normal Total
4000 8 32 40
EDIT - If you'd rather a PIVOT
Select Emp_ID
,Absence
,Normal
,Total = Absence + Normal
From YourTable
Pivot (sum( Hours ) for Wage_Type in ([Absence],[Normal] ) ) pvt
Related
i have a table that looks like this:
id position value
5 senior 10000
6 senior 20000
8 senior 30000
9 junior 5000
4 junior 7000
3 junior 10000
It is sorted by position and value (asc) already. I want to calculate the number of seniors and juniors that can fit in a budget of 50,000 such that preference is given to seniors.
So for example, here 2 seniors (first and second) + 3 juniors can fit in the budget of 50,000.
id position value cum_sum
5 senior 10000 10000
6 senior 20000 30000
8 senior 30000 60000 ----not possible because it is more than 50000
----------------------------------- --- so out of 50k, 30k is used for 2 seniors.
9 junior 5000 5000
4 junior 7000 12000
1 junior 7000 19000 ---with the remaining 20k, these 3 juniors can also fit
3 junior 10000 29000
so the output should look like this:
juniors seniors
3 2
how can i achieve this in sql?
Here's one possible solution: DB Fiddle
with seniorsCte as (
select id, position, value, total
from budget b
inner join (
select id, position, value, (sum(value) over (order by value, id)) total
from people
where position = 'senior'
) as s
on s.total <= b.amount
)
, juniorsCte as (
select j.id, j.position, j.value, j.total + r.seniorsTotal
from (
select coalesce(max(total), 0) seniorsTotal
, max(b.amount) - coalesce(max(total), 0) remainingAmount
from budget b
cross join seniorsCte
) as r
inner join (
select id, position, value, (sum(value) over (order by value, id)) total
from people
where position = 'junior'
) as j
on j.total <= r.remainingAmount
)
/* use this if you want the specific records
select *
from seniorsCte
union all
select *
from juniorsCte
*/
select (select count(1) from seniorsCte) seniors
, (select count(1) from juniorsCte) juniors
From your question I suspect you're familiar with window functions; but in case not; the below query pulls back all rows from the people table where the position is senior, and creates a column, total which is our cumulative total of the value of the rows returned, starting with the lowest value, ascending (then sorting by id to ensure consistent behaviour if there's multiple rows with the same value; though that's not strictly required if we're happy to get those in an arbitrary order).
select id, position, value, (sum(value) over (order by value, id)) total
from people
where position = 'senior'
The budget table I just use to hold a single row/value saying what our cutoff is; i.e. this avoids hardcoding the 50k value you mentioned, so we can easily amend it as required.
The common table expressions (CTEs) I've used to allow us to filter our juniors subquery based on the output of our seniors subquery (i.e. as we only want those juniors up to the difference between the budget and the senior's total), whilst allowing us to return the results of juniors and seniors independently (i.e. if we wanted to return the actual rows, rather than just totals, this allows us to perform a union all between the two sets; as demonstrated in the commented out code.
For it to work, the sum has to be not only cumulative, but also selective. As mentioned in the comment, you can achieve that with a recursive cte: online demo
with recursive
ordered as --this will be fed to the actual recursive cte
( select *,
row_number() over (order by position desc,value asc)
from test_table)
,recursive_cte as
( select id,
position,
value,
value*(value<50000)::int as cum_sum,
value<50000 as is_hired,
2 as next_i
from ordered
where row_number=1
union
select o.id,
o.position,
o.value,
case when o.value+r.cum_sum<50000 then o.value+r.cum_sum else r.cum_sum end,
(o.value+r.cum_sum)<50000 as is_hired,
r.next_i+1 as next_i
from recursive_cte r,
ordered o
where o.row_number=next_i
)
select count(*) filter (where position='junior') as juniors,
count(*) filter (where position='senior') as seniors
from recursive_cte
where is_hired;
row_number() over () is a window function
count(*) filter (where...) is an aggregate filter. It's a faster variant of the sum(case when expr then a else 0 end) or count(nullif(expr)) approach, for when you only wish to sum a specific subset of values. That's just to put those in columns as you did in your expected result, but it could be done with a select position, count(*) from recursive_cte where is_hired group by position, stacked.
All it does is order your list according to your priorities in the first cte, then go through it row by row in the second one, collecting the cumulative sum, based on whether it's still below your limit/budget.
postgresql supports window SUM(col) OVER()
with cte as (
SELECT *, SUM(value) OVER(PARTITION BY position ORDER BY id) AS cumulative_sum
FROM mytable
)
select position, count(1)
from cte
where cumulative_sum < 50000
group by position
An other way to do it to get results in one row :
with cte as (
SELECT *, SUM(value) OVER(PARTITION BY position ORDER BY id) AS cumulative_sum
FROM mytable
),
cte2 as (
select position, count(1) as _count
from cte
where cumulative_sum < 50000
group by position
)
select
sum(case when position = 'junior' then _count else null end) juniors,
sum(case when position = 'senior' then _count else null end) seniors
from cte2
Demo here
This example of using a running total:
select
count(case when chek_sum_jun > 0 and position = 'junior' then position else null end) chek_jun,
count(case when chek_sum_sen > 0 and position = 'senior' then position else null end) chek_sen
from (
select position,
20000 - sum(case when position = 'junior' then value else 0 end) over (partition by position order by value asc rows between unbounded preceding and current row ) chek_sum_jun,
50000 - sum(case when position = 'senior' then value else 0 end) over (partition by position order by value asc rows between unbounded preceding and current row ) chek_sum_sen
from test_table) x
demo : https://dbfiddle.uk/ZgOoSzF0
I have 5 different tables stored in Hive, and I would like to know how to create a new table, called total_counts which has 5 columns each with the total row count from the individual tables. So, something like
My data is road flights for each year from 2015 to 2019, so I would like a table which just gives me the total number of accidents in each year.
I have tried variations of the following:
create table total_counts
as select COUNT(*)
from flights_2014 as "2014_count", flights_2015 as "2015_count;
I can get the counts for an individual year, but I can't seem to give the columns a heading, nor can I figure out how to do it for all my tables.
Thanks.
Calculate counts in sub-queries and do cross joins if you want to store data in columns
CREATE TABLE total_counts AS
SELECT 2015_count.cnt as 2015_count, 2016_count.cnt as 2016_count, ...
FROM (SELECT COUNT(*) cnt FROM flights_2015) AS 2015_count
CROSS JOIN
(SELECT COUNT(*) cnt FROM flights_2016) AS 2016_count
...
Or the same using UNION ALL + aggregation:
CREATE TABLE total_counts AS
SELECT max(case when yr=2015 then cnt else 0 end) 2015_count,
max(case when yr=2016 then cnt else 0 end) 2016_count,
...
FROM (
SELECT COUNT(*) cnt, 2015 yr FROM flights_2015
UNION ALL
SELECT COUNT(*) cnt, 2016 yr FROM flights_2016
...
) u
CREATE TABLE total_counts AS
SELECT
(SELECT COUNT(*) FROM flights_2015) AS 2015_count,
(SELECT COUNT(*) FROM flights_2016) AS 2016_count;
etc.
I rarely have had to use pivot in SQL and am having trouble wrapping my head around it. I have query that produces a table of patients and insurance for a medical facility. I want to return a single row that shows all of the insurances that the patient has.
Sample data:
ID Insurance
1840 Medicaid Of New York
1841 Affinity Health Plan
1841 Medicaid Of New York
1842 Fidelis Care
1842 Medicaid Of New York
The goal is to show the data like this:
I'm understand the basic functionality of pivot, but don't have a lot of time to figure out how to achieve the results above. I haven't achieved anything close to working results, which could be either lack of experience or fatigue at this point. Any help would be appreciated.
something like this
with data as (
select id,insurance ,row_number() over ( partition by id order by insurance) rowid
)
select d.id , [1] as ins1,[2] ins2,[3]ins3,[4] ins4
from data d
pivot
(max(insurance)
for rowid in ([1],[2],[3],[4])
)pv
I prefer conditional aggregation where possible:
SELECT ID,
MAX(CASE WHEN RN = 1 THEN INSURANCE ELSE '' END) AS INSURANCE1,
MAX(CASE WHEN RN = 2 THEN INSURANCE ELSE '' END) AS INSURANCE2,
MAX(CASE WHEN RN = 3 THEN INSURANCE ELSE '' END) AS INSURANCE3,
MAX(CASE WHEN RN = 4 THEN INSURANCE ELSE '' END) AS INSURANCE4
FROM (
SELECT ID,
INSURANCE,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY INSURANCE) RN
FROM YourTable
) A
GROUP BY ID
This is a good read on conditional aggregration, or Cross Tabs, versus PIVOT
If you have at most two values (as suggested by your sample data and results), you can use min() and max():
select id,
min(insurance) as insurance1,
(case when min(insurance) <> max(insurance) then max(insurance)
end) as insurance2
from t
group by id;
Here is what you are looking for. This will flatten your records as desired.
SELECT
ID, Insurance1, Insurance2, Insurance3, Insurance4
FROM ( SELECT
ID,
Insurance,
RN = 'Insurance' +
CAST(ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Insurance) AS CHAR(1))
FROM MyDB.MySchema.MyTable
) AS d
PIVOT(
MAX(Insurance)
for RN
in (Insurance1, Insurance2, Insurance3, Insurance4)
) AS p;
I'm trying to write a query that gives me the count of records where execoffice_status=1
(could equal =0 too).
I want to output the results by using a different table employee which gives me their
names.
The query I wrote seems to give me some results but gives me all the records in the table
even where execoffice_status=0 (not sure how I would add that to the query).
What I'm trying to get out off the query is the count of records that execoffice_status=1
and from what year (execoffice_date), what eventually i would like from the query is the top 10
from each year (order by year).
With the query below I get all the record and even where execoffice_status=0
query:
SELECT *
FROM (
select ROW_NUMBER() OVER(PARTITION BY e.emp_namelast order by year(c.execoffice_date) desc ) as RowNum,
year(c.execoffice_date) as year, e.emp_nameFirst + ' ' + e.emp_namelast as fullname, count(c.execoffice_status) as stars
from phonelist.dbo.employee e
join intranet.dbo.CSEReduxResponses c on c.employee = e.emp_id
group by emp_namelast, emp_namefirst, year(c.execoffice_date)
) a
order by year
Here is a http://sqlfiddle.com/#!3/79f253/1 that I made with some dummy data.
For the first bit of your question you can simply add a where clause.
where c.execoffice_status=1
To get the top values for each year, Rank can accomplish this:
SELECT *
FROM (
select RANK() OVER(PARTITION BY year(c.execoffice_date) order by e.emp_namelast desc ) as Rank,
year(c.execoffice_date) as year, e.emp_nameFirst + ' ' + e.emp_namelast as lastName, sum(c.execoffice_status) as stars
from employee e
join CSEReduxResponses c on c.employeee = e.emp_id
where c.execoffice_status=1
group by emp_namelast, emp_namefirst, year(c.execoffice_date)
) a
where rank <= 2
order by year
fiddle
This numbers the users by their stars and gives you the top 2 for each year. (for 10 just <= 10)
SELECT * FROM (
select ROW_NUMBER() OVER(PARTITION BY e.emp_namelast order by year(c.execoffice_date) desc ) as RowNum,
year(c.execoffice_date) as year, e.emp_nameFirst + ' ' + e.emp_namelast as lastName, sum(c.execoffice_status) as stars
from employee e
join CSEReduxResponses c on c.employeee = e.emp_id
where c.execoffice_status <> 0
group by emp_namelast, emp_namefirst, year(c.execoffice_date)
) a WHERE RowNum<=10 order by year
Here is how I understood your requirements.
Get count of records for each employee per year where execoffice_status is 1
execoffice_status can be one or zero
in this case, you can use sum and group by, if execoffice_status can be another number other than one or zero, then we would need to use rownum and count, instead of sum and group by
let me know if this does what you want.
select * from(
select a.employeEe,a.execoffice_date, SUM(a.execoffice_status) execoffice_status_count
from CSEReduxResponses a
group by a.employeEe,execoffice_date
) a
left outer join employee b
on b.emp_id = a.employeee
where EXECOFFICE_STATUS_COUNT > 0
order by execoffice_date desc;
also if you want to get the top 10 rows, I think with sql server you do Select TOP 10 field1, field2, field3 from table
I am trying to select the name of a field which occurs the most often in a table and in which a certain value is true.
Select
Max(Count(Name))
From
EmployeeTreats
Where
Donut = "Yes"
And
AmountEaten >= 10
Error: Cannot perform an aggregate
function on an expression containing
an aggregate or a subquery.
What I am looking for is obviously something like: Edward has eaten the most with a sum total of 45
Name
Edward
According to your initial question:
select top (1)
[Name]
, count(1) as Cnt
from Employees
where Donut = 'yes'
and AmountEaten >= 10
group by [Name]
order by Cnt desc;
After your edit:
select top (1)
[Name]
, sum(AmountEaten) as TotalEaten
from Employees
where Donut = 'yes'
group by [Name]
order by TotalEaten desc;
This will handle the case where there's more than one Name with the same count.
select Name, count(*)
from ExployeeTreats
where Donut = "Yes" and AmountEaten >= 10
group by Name
having count(*) >= ALL ( select count(*)
from EmployeeTreats
where Donut = "Yes" and AmountEaten >= 30
group by Name )
The trick is to group by name. Then order by count(*) descending, and clip off the top 1.
select top 1 max(name)
from employeeTreats
where donut='Yes'
and amountEaten >= 10
group by name
order by count(*) desc