SQL server average age in two different rows - sql

I have this query, where I created two columns for 'female' and 'male'
SELECT
ID,
UserName,
concat(FirstName,' ',LastName) as Name,
iif(substring(ID,10,1) % 2 =0,'Female', 'Male') as Gender
INTO NewUsers1
FROM Users
Now, I want to get the Average age for both genders. I want to separate this into two columns named ' gender' and 'average age' with one row for 'female average age' and one row for 'male average age'
How can I do this?
The age is seen from the column ID, where the number is the date of birth.
ID
500603-4268 <-- birth year is 3rd June 1950.
500607-6521 <-- birth year is 7th June 1950.
530407-7989 <-- birth year is 4th April 1953.
530720-7675
540430-4887

This gives you average ages in two columns without loosing other columns:
select
*
, avg(case when Gender = 'Male' then null else age end) over(partition by Gender) avgAge_Female
, avg(case when Gender = 'Female' then null else age end) over(partition by Gender) avgAge_Male
from
(
select
f1.*
, datediff(
year
, cast(
concat(
concat('19', left(f1.vl, 2))
, '-'
, right(left(f1.vl, 4), 2)
, '-'
, right(f1.vl, 2)
)
as date)
, getdate()
) age
from
(
select
ID,
UserName,
concat(FirstName,' ',LastName) as Name,
left(ID, charindex('-', ID) - 1) vl,
iif(substring(ID,10,1) % 2 =0,'Female', 'Male') as Gender
from Users
) f1
) g1
Edit: Combined into one column version:
Note: I've used subqueries to make it easy to understand so it is not an optimized query.
select
h1.ID
, h1.UserName
, h1.Name
, h1.Gender
, isnull(h1.avgAge_Male, h1.avgAge_Female) avgAge_MaleOrFemale
from
(
select
*
, avg(case when Gender = 'Male' then null else age end) over(partition by Gender) avgAge_Female
, avg(case when Gender = 'Female' then null else age end) over(partition by Gender) avgAge_Male
from
(
select
f1.*
, datediff(
year
, cast(
concat(
concat('19', left(f1.vl, 2))
, '-'
, right(left(f1.vl, 4), 2)
, '-'
, right(f1.vl, 2)
)
as date)
, getdate()
) age
from
(
select
ID,
UserName,
concat(FirstName,' ',LastName) as Name,
left(ID, charindex('-', ID) - 1) vl,
iif(substring(ID,10,1) % 2 =0,'Female', 'Male') as Gender
from Users
) f1
) g1
) h1

You can use conditional aggregation:
SELECT AVG(CASE WHEN substring(ID, 10, 1) % 2 = 0 THEN AGE END) as AVG_AGE_FEMALE,
AVG(CASE WHEN substring(ID, 10, 1) % 2 = 1 THEN AGE END) as AVG_AGE_MALE
FROM Users;

Related

how to show repeated date value as null in SQL Server?

I have a SQL Query
SELECT DISTINCT DENSE_RANK()
OVER(ORDER BY bsContract.ContractNumber DESC) AS RANKS,
ROW_NUMBER()
OVER(PARTITION BY bsContract.ContractNumber ORDER BY bsContract.ContractNumber)
AS RowsNumber,
dbo.bsContract.ContractNumber,
Advertiser.OrganizationName AS Advertiser,
JobName AS Campaign,
bsContract.FromDate AS StartDate,
Advertiser.OrganizationName AS Client,
(ISNULL(bsContract.FirstName, '') + ' ' +
bsContract.LastName) AS ClientRep,
Advertiser.OrganizationName AS Vendor,
(ISNULL(bsContract.FirstName, '') + ' ' +
bsContract.LastName) AS 'VendorRep',
DocumentType,
FileType
INTO #temp
FROM dbo.bsContract
INNER JOIN Organization AS Advertiser
ON Advertiser.OrganizationID = bsContract.OrganizationID
WHERE FileType IS NULL
AND bsContract.ContractNumber = 'CR6520'
AND bsDocument.SellerID = 3632
SELECT DISTINCT Ranks,
(CASE
WHEN RowsNumber = 1 THEN
ContractNumber
ELSE
''
END) AS ContractNumber,
(CASE
WHEN RowsNumber = 1 THEN
Advertiser
ELSE
''
END) AS Advertiser,
(CASE
WHEN RowsNumber = 1 THEN
Campaign
ELSE
''
END) AS Campaign,
(CASE
WHEN RowsNumber = 1 THEN
StartDate
ELSE
(select convert(datetime, NULL))
END) AS StartDate,
Vendor,
VendorRep
FROM #temp
ORDER BY ContractNumber DESC
Everything is fine it gives the result. The first row gives the value
of Contract Number, Advertiser Campaign, StartDate, Vendor and Vendor
Rep. From 2nd row the value of ContractNumber, Advertiser, Campaign,
StartDate is same so it is shown as Blank. But the first row of the
result is displayed twice due to the Different StartDate. Refer attached screenshot:
How can I solve this issue?

Sql ISNULL condition in Sql Pivot and Sql case

I searched for many solutions on SO and elsewhere but couldn't quite understand how to write a query for my problem.
Anyway my query looks like below
SELECT * FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
and the result looks like this
Id Date Name Current Due30 Due60 Due90
----------- ---------- --------------------------------------------
1 2016-04-03 Alan NULL NULL NULL 110.00
2 2016-05-02 TC NULL NULL 30.00 NULL
where should i insert IsNull condition to be able to remove the null in the result and add a zero there.
I tried inserting IsNull in the pivot query but we all know that is not meant to work
You have to add it repetitively in the final SELECT, when you replace the SELECT * (which should only exist in ad-hoc queries or EXISTS tests) with the column list:
SELECT
Id,
Date,
Name,
COALESCE([Current],0) as [Current],
COALESCE(Due30,0) as Due30,
COALESCE(Due60,0) as Due60,
COALESCE(Due90,0) as Due90
FROM
(
SELECT Id, Date, Name, Amount,
CASE
WHEN DATEDIFF(DAY,Date,GETDATE()) <=0
THEN 'Current'
WHEN DATEDIFF(DAY,Date,GETDATE()) <30
THEN 'Due30'
WHEN DATEDIFF(DAY,Date,GETDATE()) <60
THEN 'Due60'
ELSE 'Due90'
END AS [Age]
FROM Statement
WHERE (Amount <> 0)
) AS S
PIVOT
(
SUM(Amount)
FOR[Age] IN ([Current],[Due30],[Due60],[Due90])
) P
I've also used COALESCE since it's generally the preferred option (ANSI standard, extends to more than two arguments, applies normal type precedence rules) instead of ISNULL.
SELECT Id
, [Date]
, Name
, [Current] = SUM(CASE WHEN val <= 0 THEN Amount ELSE 0 END)
, Due30 = SUM(CASE WHEN val < 30 THEN Amount ELSE 0 END)
, Due60 = SUM(CASE WHEN val < 60 THEN Amount ELSE 0 END)
, Due90 = SUM(CASE WHEN val >= 60 THEN Amount ELSE 0 END)
FROM dbo.[Statement] t
CROSS APPLY (
SELECT val = DATEDIFF(DAY, [Date], GETDATE())
) s
WHERE Amount <> 0
GROUP BY Id, [Date], Name

Rewrite SQL query to return rows for each dataset

I have the following table for student's fee payments
[fee_id] INT
[user_id] INT
[payment] DECIMAL (18, 2)
[date] DATETIME
[fee_remaining] DECIMAL (18, 2)
[year] INT
[payment_method] NVARCHAR (50)
[fee_required] DECIMAL (18, 2)
This is my current query to display the number of students who have either paid, yet to pay or have partially paid their fees for the year
SELECT DISTINCT
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining = 0)
AND (YEAR = #year)) AS Fully_Paid,
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining = fee_required)
AND (YEAR = #year)) AS Unpaid,
(SELECT COUNT(*) AS Expr1
FROM fee_payments
WHERE (fee_remaining > 0)
AND (YEAR = #year)
AND (fee_remaining <> fee_required)) AS Partially_Paid
FROM fee_payments AS fee_payments_1
This is my output
Fully_Paid | Unpaid | Partially_Paid
-------------------------------------
8 | 1 | 5
Is it at all possible to have my output displayed as follows?
Status | Total
----------------------------
Fully Paid | 8
Unpaid | 1
Partially Paid | 5
Any help would be greatly appreciated
Use a case expression to assign the required status to each row and group by the calculated column.
select status, count(*) as total
from (
SELECT
case when fee_remaining = 0 then 'fully_paid'
when fee_remaining <> fee_required then 'partially_paid'
when fee_remaining = fee_required then 'unpaid'
end as status
FROM fee_payments
WHERE YEAR = #year) t
group by status
Also note this assumes fee_remaining and fee_required are non null values. If they can be null, use coalesce to handle them when comparing.
So without completely restructuring your query into something more efficient like kvp's answer, you could UNION each of the results instead of using them each as a sub-query:
SELECT 'Fully Paid' AS Status, COUNT(*) AS Total
FROM fee_payments
WHERE (fee_remaining = 0) AND (YEAR = #year)
UNION
SELECT 'Unpaid', COUNT(*)
FROM fee_payments
WHERE (fee_remaining = fee_required) AND (YEAR = #year)
UNION
SELECT 'Partially Paid', COUNT(*)
FROM fee_payments
WHERE (fee_remaining > 0) AND (YEAR = #year) AND (fee_remaining <> fee_required)
Your code appears to have more than one row per year. It seems like the last row would be the most informative, so I'm thinking something like this:
select sum(case when fee_remaining = 0 then 1 else 0 end) as FullyPaid,
sum(case when fee_remaining < fee_required then 1 else 0 end) as PartiallyPaid,
sum(case when fee_remaining = fee_required then 1 else 0 end) as Unpaid
from (select fp.*,
row_number() over (partition by user_id order by date desc) as seqnum
from fee_payments fp
where YEAR = #year
) fp
where seqnum = 1;
SELECT 'Fully Paid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining = 0
AND YEAR = #year
UNION ALL
SELECT 'Unpaid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining = fee_required
AND YEAR = #year
UNION ALL
SELECT 'Partially Paid' as Status, COUNT(*) AS Total
FROM fee_payments
GROUP BY year
WHERE fee_remaining > 0
AND YEAR = #year
AND fee_remaining <> fee_required

Divide and sum in SQL

I got this code and in this code I do a sum of the slow and fast driver. My Problem is I must divide this sum with the normal driver. I donĀ“t know how I can do a division in this statement:
Select *
FROM (
Select date as Datetime, tevent.name as Event, level = case
when levelname = 'High' then 'High'
when levelname = 'Normal' then 'Normal'
when shiftname = 'Low' then 'Low'
end, SUM(value) as sum
from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('Drive Fast', 'Drive Slow')
and date > getdate() -1
and tevent.Name in ('E01','E02','E03','E04','E05','E06','E07','E08')
and CalName = 'Drive'
group by tevent.name, date, levelname
) as s
PIVOT
(
SUM(sum)
FOR Event IN (E01,E02,E03,E04,E05,E06,E07,E08)
) as p
order by Datetime, level
And Then I put the same Select statement with the normal driver :
... from tCount inner join tEvent ON tCount.eventid = tevent.id
where Name in ('drive normal') ...
And I would like to make a division like this:
(Sum('drive fast' + 'drive slow')/Sum('drive normal')) * 100
There is a simpler way to include different cases in different sums inside a SQL statement: sum a case, like in the below calculation of percent:
Select ...
, SUM(case Name
when 'drive fast' then Value
when 'drive slow' then value
else 0 end)
/ SUM(case Name
when 'drive normal' then value
else 0 end) * 100 as percentage
from ...
where ...
group by ...;
As I lack data to test this code, I created a query on the CARS table SAS delivers as training material, implementing the same principle.
select Cylinders
, sum(case origin when 'USA' then EngineSize
when 'Asia' then EngineSize
else 0.0 end)
/ sum(case origin when 'Europe' then EngineSize
else 0.0 end)
* 100 as percentage
from sasHelp.cars
where Cylinders in (4, 5, 6, 12)
group by Cylinders

Need single row in sql query despite row count is 0

Below is my query which returns no row and this is correct as per database records.
SELECT CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
YEAR(ReceivedDate) AS CurrentYear ,
CONVERT(VARCHAR, COUNT(CASE WHEN MONTH(ReceivedDate) = 1 THEN 1
ELSE 0
END)) AS Jan
FROM RootTable
WHERE Customerid = 12742
AND YEAR(ReceivedDate) = 2014
GROUP BY CustomerID ,
RForLocation ,
YEAR(ReceivedDate)
But my requirement is that I should get a blank row with customerId, CustType,CurrentYear and Jan Count as Zero (0)
Below is my requirement
CustomerId CustType CurrentYear Jan
12742 Forwarder 2014 0
Thanks
Please Help
SELECT CustomerID
,'Forwarder' AS CustType
,RForLocation
,YEAR(ReceivedDate) AS CurrentYear
,ISNULL(
NULLIF(
COUNT(CASE WHEN MONTH(ReceivedDate) = 1 THEN 1 ELSE 0 END)
, 0)
, '')
AS Jan
FROM RootTable
WHERE Customerid = 12742
AND YEAR(ReceivedDate) = 2014
GROUP BY CustomerID ,
RForLocation ,
YEAR(ReceivedDate)
Whatever I could understand, please find the script and the updated query, with which, I tried it.
Created the table:
Create table RootTable
(CustomerID int,
RForLocation varchar(20),
ReceivedDate datetime)
Inserted the data:
insert into RootTable
Values(1, 'A', '1-Jan-2013'),
(2, 'A', '1-Feb-2013'),
(3, 'B', '1-Jan-2013'),
(4, 'B', '1-Mar-2013')
Query to fetch the records :
SELECT CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
'2014' AS CurrentYear ,
CONVERT(VARCHAR, COUNT(CASE WHEN MONTH(ReceivedDate) = 1 AND YEAR(ReceivedDate) = 2014 THEN 1
ELSE null
END)) AS Jan
FROM RootTable
WHERE Customerid = 1
GROUP BY CustomerID ,
RForLocation;
Since I want record for customer even if he does not have any record for specific year, I don't need to give condition of date in where clause, instead I can do the counting based on year, which will give me required output.
Hope it helps.
Change your CASE statement
CONVERT(VARCHAR, CASE WHEN count(MONTH(ReceivedDate) = 1)
THEN count(MONTH(ReceivedDate) = 1)
ELSE 0
end
SELECT
RootTable.CustomerID ,
'Forwarder' AS CustType ,
RForLocation ,
YEAR(ReceivedDate) AS CurrentYear,
SUB1.MON_COL,
CASE WHEN sub1.CNT IS NULL THEN 0 ELSE sub1.CNT END AS CNT
FROM RootTable
LEFT JOIN
(
SELECT COUNT(1) CNT, CustomerId,MONTH(ReceivedDate) AS MON_COL,YEAR(ReceivedDate) AS YEAR_COL
FROM RootTable
GROUP BY CustomerId,MONTH(ReceivedDate),YEAR(ReceivedDate)
) sub1 ON sub1.CustomerID = RootTable.CustomerId AND MONTH(ReceivedDate) = sub1.MON_COL AND YEAR(ReceivedDate) = sub1.YEAR_COL
WHERE RootTable.CustomerId=2