sql query grouping with condition - sql

there is a table with employee numbers(column) and monthy payments(column)with paramcode(column) as basic, vda etc.. and amounts(column) corresponding to each paramcode.
empno. month paramcode amount
1 jan basic 788
1 feb vda 232
1 march pf 12
now this is actual question
Write a query to display sum of BASIC + VDA + HRA for an employee Payment where the employee has been paid PF in the payment
(do not use sub queries , Joins and Set operators)
My answer is
SELECT EmployeeNumber,paramcode, SUM(ActualAmount) AS S FROM pay
WHERE ParamCode IN ('BASIC','VDA','HRA','pf')
GROUP BY GROUPING SETS((EmployeeNumber,ParamCode))
ORDER BY EmployeeNumber
but i want to eliminate the employee numbers with param code as 'pf'

If I understood you right use SQL except tag like this except ParamCode in ('pf')

You are actually adding 'PF' in ParamCode, this is the correct query:
SELECT EmployeeNumber,paramcode, SUM(ActualAmount) AS S FROM pay
WHERE ParamCode IN ('BASIC','VDA','HRA')
GROUP BY GROUPING SETS((EmployeeNumber,ParamCode))
ORDER BY EmployeeNumber

Related

How can I get the lastest Date for each record? [duplicate]

This question already has answers here:
Retrieving last record in each group from database - SQL Server 2005/2008
(2 answers)
Get top 1 row of each group
(19 answers)
Closed 4 months ago.
I have this table in SQL Server:
DepartmentID
Department
EmployeeID
Rate
RateModifiedDate
16
Executive
234
39.0600
2009-01-31 00:00:00.000
16
Executive
234
48.5577
2011-11-14 00:00:00.000
16
Executive
234
60.0962
2012-01-29 00:00:00.000
16
Executive
1
125.5000
2009-01-14 00:00:00.000
I want the latest RateModifiedDate for each employee like this:
DepartmentID
Department
EmployeeID
Rate
RateModifiedDate
16
Executive
234
60.0962
2012-01-29 00:00:00.000
16
Executive
1
125.5000
2009-01-14 00:00:00.000
You are on the right track to use MAX to find the latest date.
The reason why your query doesn't work is that you need to find more than one latest data. You require the latest date per employee. This can be done using a subquery:
SELECT DepartmentID, Department, EmployeeID, rate, RateModifiedDate
FROM yourtable y1
WHERE RateModifiedDate =
(SELECT MAX(y2.RateModifiedDate)
FROM yourtable y2
WHERE y1.EmployeeID = y2.EmployeeID)
ORDER BY RateModifiedDate DESC;
The main query will select all columns that should be shown and sort them by the date, beginning with the latest one. The sub query will find the latest date per employee. The WHERE clause of the main query will make sure that only those entries will be selected whose date matches the latest date of the current employee. Thus, the outcome will be exactly as requested.
This query will be executed on each DB type since it doesn't contain DB type-specific syntax.
Beside this general option, common DB's provide window functions like RANK that make such things "on their own". So you could also create such a query:
SELECT DepartmentID, Department, EmployeeID, rate, RateModifiedDate
FROM (SELECT DepartmentID,
Department,
EmployeeID,
rate,
RateModifiedDate,
RANK() OVER(PARTITION BY EmployeeID ORDER BY RateModifiedDate DESC) dest_rank
FROM yourtable) sub
WHERE dest_rank = 1
ORDER BY RateModifiedDate DESC;
The outcome of this query will be the same.
The PARTITION BY clause in the sub query will group the data by the employee, the ORDER BY clause will sort it by the date, beginning with the latest.
The WHERE clause of the main query will take only the data having the latest date of the sorted list from the subquery.
This query will not be executed on each DB type because the syntax and naming of window functions often differ, also older DB versions might not provide them.
This will be the result of both queries above:
DepartmentID
Department
EmployeeID
rate
RateModifiedDate
16
Executive
234
60.0962
2012-01-29
16
Executive
1
125.5000
2009-01-14
Try out here: db<>fiddle

Can I query a aggregated query and a specific row's query when using subqueries?

I am new to SQL and I wanted to return the results of a specific value and the average of similar values. I have gotten the average part working but I'm not sure how to do the specific value part.
For more context, I have a list of carbon emissions by companies. I wanted the average of a industry based on a company's industry(working perfectly below), but I am not sure how to add the specific companies info.
Here's my query:
SELECT
year, AVG(carbon) AS AVG_carbon,
-- carbon as CompanyCarbon, <--my not working attempt
FROM
"company"."carbon" c
WHERE
LOWER(c.ticker) IN (SELECT LOWER(g4.ticker)
FROM "company"."General" g4
WHERE industry = (SELECT industry
FROM "company"."General" g3
WHERE LOWER(g3.ticker) = 'ibm.us'))
GROUP BY
c.year
ORDER BY
year ASC;
The current result is:
year avg_carbon
--------------------------------
1998 7909.0000000000000000
1999 19465.500000000000
2000 19478.000000000000
2001 182679.274509803922
2002 179821.156862745098
My desired output is:
year avg_carbon. Carbon
---------------------------------------
1998 7909.0000000000000000 343
1999 19465.500000000000 544
2000 19478.000000000000 653
2001 182679.274509803922 654
2002 179821.156862745098 644
(adding the carbon column based on "IBM" carbon
Here's my Carbon table:
ticker year carbon
-----------------------
hurn.us 2016 6282
hurn.us 2015 6549
hurn.us 2014 5897
hurn.us 2013 5300
hurn.us 2012 5340
ibm.us 2019 1496520
ibm.us 2018 1438365
Based on my limited knowledge, I think my where the statement is causing the problem. Right now I took at a company, get a list of tickers/identifiers of the same industry then create an average for each year.
I tried to just call the carbon column but I think because it's processing the list of tickers, it's not outputting the result I want.
What can I do? Also if I'm making any other mistakes you see above please let me know.
Sample data nd output do not match. So I can't say for sure but this might be the answer you are looking for.
select year, AVG(carbon) AS AVG_carbon,
max(case when lower(ticker) = 'ibm.us' then carbon else 0 end) as CompanyCarbon
from "company"."carbon" c
GROUP BY c.year
order by year ASC;
This will select max(carbon) for any year as CompanyCarbon if lower(ticker) = 'ibm.us'. Average will be calculated as you did.
To select only rows having positive value in CompanyCarbon column:
select year, AVG_carbon, CompanyCarbon
from
(
select year, AVG(carbon) AS AVG_carbon,
max(case when lower(ticker) = 'ibm.us' then carbon else 0 end) as CompanyCarbon
from "company"."carbon" c
GROUP BY c.year
order by year ASC;
)t where carbon > 0
Similar to the answer that Kazi provided you can use the FILTER syntax on an aggregate which makes it a bit more readable than the case/when IMO.
SELECT
year,
AVG(carbon) as avg_carbon,
MAX(carbon) FILTER (WHERE ticker = 'ibm.us') as company_carbon
FROM company_carbon
GROUP BY year
ORDER by year;

How to add custom YoY field to output?

I'm attempting to determine the YoY growth by month, 2017 to 2018, for number of Company bookings per property.
I've tried casting and windowed functions but am not obtaining the correct result.
Example Table 1: Bookings
BookID Amnt BookType InDate OutDate PropertyID Name Status
-----------------------------------------------------------------
789555 $1000 Company 1/1/2018 3/1/2018 22111 Wendy Active
478141 $1250 Owner 1/1/2017 2/1/2017 35825 John Cancelled
There are only two book types (e.g., Company, Owner) and two Book Status (e.g., Active and Cancelled).
Example Table 2: Properties
Property ID State Property Start Date Property End Date
---------------------------------------------------------------------
33111 New York 2/3/2017
35825 Michigan 7/21/2016
The Property End Date is blank when the company still owns it.
Example Table 3: Months
Start of Month End of Month
-------------------------------------------
1/1/2018 1/31/2018
The previous developer created this table which includes a row for each month from 2015-2020.
I've tried many various iterations of my current code and can't even come close.
Desired Outcome
I need to find the YoY growth by month, 2017 to 2018, for number of Company bookings per property. The stakeholder has requested the output to have the below columns:
Month Name Bookings_Per_Property_2017 Bookings_Per_Property_2018 YoY
-----------------------------------------------------------------------
The number of Company bookings per property in a month should be calculated by counting the total number of active Company bookings made in a month divided by the total number of properties active in the month.
Here is a solution that should be close to what you need. It works by:
LEFT JOINing the three tables; the important part is to properly check the overlaps in date ranges between months(StartOfMonth, EndOfMonth), bookings(InDate, OutDate) and properties(PropertyStartDate, PropertyEndDate): you can have a look at this reference post for general discussion on how to proceed efficiently
aggregating by month, and using conditional COUNT(DISTINCT ...) to count the number of properties and bookings in each month and year. The logic implicitly relies on the fact that this aggregate function ignores NULL values. Since we are using LEFT JOINs, we also need to handle the possibility that a denominator could have a 0 value.
Notes:
you did not provide expected results so this cannot be tested
also, you did not explain how to compute the YoY column, so I left it alone; I assume that you can easily compute it from the other columns
Query:
SELECT
MONTH(m.StartOfMonth) AS [Month],
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2017 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2017,
COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN b.BookID END)
/ NULLIF(COUNT(DISTINCT CASE WHEN YEAR(StartOfMonth) = 2018 THEN p.PropertyID END), 0)
AS Bookings_Per_Property_2018
FROM months m
LEFT JOIN bookings b
ON m.StartOfMonth <= b.OutDate
AND m.EndOfMonth >= b.InDate
AND b.status = 'Active'
AND b.BookType = 'Company'
LEFT JOIN properties p
ON m.StartOfMonth <= COLAESCE(p.PropertyEndDate, m.StartOfMonth)
AND m.EndOfMonth >= p.PropertyStartDate
GROUP BY MONTH(m.StartOfMonth)

calculating month salary for an employee

I am working on my Database in MS Access 2010
and i Need to build a query to Calculate the month salary for each Employee
it goes like this :
Input from user , which Year
Input from user again , which Month
Show Every Employee's Salary for the Input date
There are 2 Tables in the Query : Shifts , Employees
Shifts has a field for EmployeeID and a field for Day
Day field format is : Short Date
The problem is i don't know how to access the Month and the Year only !
I know that this is completely wrong , but i wanna do something like this:
SELECT
FROM EmployeesTBL INNER JOIN ShiftsTBL ON EmployeesTBL.EmployeeID = ShiftsTBL.EmployeeID
WHERE
Year(ShiftsTBL.Day)=[Enter Year]
AND
Month(ShiftsTBL.Day)=[Enter Month]
;
What do i need to write after SELECT to get the Sum of all Shifts and divide it by number of days the emp worked
Note : in the Shifts Table , i have EntryDate and ExitDate for every shift
Access has a bunch of built in date functions. I believe Month(date) and Year(date) will give you what you need.
Something like
SELECT EmpName
FROM Employees, Shifts
WHERE Employees.EmployeeID = Shifts.EmployeeID
AND
Month(Shifts.Day) = INPUT2.VALUE
AND
Year(Shifts.Day) = INPUT1.VALUE
should get you what you want!
EDIT: Aggregation: how this works will depend on how your database is set up. I think I understand you want to sum the hours worked and divide by the number of days?
If so, you will use Sum() and Count(). And you will Group By EmployeeID
SELECT Sum(Shifts)/Count(DaysWorked) AS SumDividedByCount
FROM EmployeesTBL INNER JOIN ShiftsTBL
ON EmployeesTBL.EmployeeID = ShiftsTBL.EmployeeID
WHERE
Year(ShiftsTBL.[Day])=[Enter Year]
AND
Month(ShiftsTBL.[Day])=[Enter Month]
GROUP BY EmployeeID
I used the WHERE clause because I think the results need to be filtered before they're grouped. If the results needed to be filtered after they were grouped, the HAVING clause would be used (and would go AFTER the GROUP BY)

SQL: Can GROUP BY contain an expression as a field?

I want to group a set of dated records by year, when the date is to the day. Something like:
SELECT venue, YEAR(date) AS yr, SUM(guests) AS yr_guests
FROM Events
...
GROUP BY venue, YEAR(date);
The above is giving me results instead of an error, but the results are not grouping by year and venue; they do not appear to be grouping at all.
My brute force solution would be a nested subquery: add the YEAR() AS yr as an extra column in the subquery, then do the grouping on yr in the outer query. I'm just trying to learn to do as much as possible without nesting, because nesting usually seems horribly inefficient.
I would tell you the exact SQL implementation I'm using, but I've had trouble discovering it. (I'm working through the problems on http://www.sql-ex.ru/ and if you can tell what they're using, I'd love to know.) Edited to add: Per test in comments, it is probably not SQL Server.
Edited to add the results I am getting (note the first two should be summed):
venue | yr | yr_guests
1 2012 15
1 2012 35
2 2012 12
1 2008 15
I expect those first two lines to instead be summed as
1 2012 50
Works Fine in SQL Server 2008.
See working Example here: http://sqlfiddle.com/#!3/3b0f9/6
Code pasted Below.
Create The Events Table
CREATE TABLE [Events]
( Venue INT NOT NULL,
[Date] DATETIME NOT NULL,
Guests INT NOT NULL
)
Insert the Rows.
INSERT INTO [Events] VALUES
(1,convert(datetime,'2012'),15),
(1,convert(datetime,'2012'),35),
(2,convert(datetime,'2012'),12),
(1,convert(datetime,'2008'),15);
GO
-- Testing, select newly inserted rows.
--SELECT * FROM [Events]
--GO
Run the GROUP BY Sql.
SELECT Venue, YEAR(date) AS yr, SUM(guests) AS yr_guests
FROM Events
GROUP BY venue, YEAR(date);
See the Output Results.
VENUE YR YR_GUESTS
1 2008 15
1 2012 50
2 2012 12
it depends of your database engine (or SQL)
to be sure (over different DB Systems & Versions), make a subquery
SELECT venue, theyear, SUM(guests) from (
SELECT venue, YEAR(date) AS theyear, guest
FROM Events
)
GROUP BY theyear
you make a subtable of
venue, date as theyear, guest
aaaa, 2001, brother
aaaa, 2001, bbrother
bbbb, 2001, nobody
... and so on
and then
count them