Calculation of Average of distinct values - sql-server

I have date in below format:
Now, I am want to display latest Post Date of each Account No, its corresponding amount, average of Amount of all previous dates including the latest date but the hack is I need to display latest Post Date based on ShareID.
I need the Amount of minimum ShareID and while taking average I have to omit amount of duplicate/same date of max(Postdate).
I need the data in below format:

You can try :
WITH
T1 AS
(
SELECT AccountNo, FundNo, MAX(PostDate) AS LastPostDate
FROM MyTable
GROUP BY AccountNo, FundNo,
),
T2 AS
(
SELECT DISTINCT AccountNo, FundNo, Amount
FROM MyTable
)
SELECT T1.AccountNo, T1.FundNo, T1.LastPostDate, T.PostDate, AVG(T2.Amount)
FROM T1
JOIN T2 ON T1.AccountNo = T2.AccountNo
JOIN MyTable AS T ON T.AccountNo = T1.AccountNo AND T.PostDate, = T1.LastPostDate
Post the DDL of your table and an example under a INSERT SQL statements to help us sto give a correct solution please.

Related

SQL query to insert stats of students to a table

I would like some help on how to write an sql server query in order to insert the monthly stats of students into a table.
My monthly stats table is something like this:
| StudentID | Year | Month | Grade1 | Grade2| Absences
Now I have another table with the Students Details like StudentID, name, etc. Also multiple other tables with grades, presence etc.
My goal is to select all studentsIDs from StudentDetails and insert them to the Monthly Stats table while I calculate Grade1, Grade2, and Absences from other multiple tables.
What is the best way to write such a query?
Do I first insert the StudentsIds, Year column and Month column with a select into query and after that, I iterate somehow through every studentid that were inserted and run update queries (for calculating rest of columns) for every studentID for the specified month and year?
I just need an example or some logic on how to achieve this.
For the the first part of inserting studentids I have this:
declare #maindate date = '20230101';
insert into Monthly_Stats (StudentID, Year, Month)
(select StudentID, AllocatedYear, AllocatedMonth
from Students_Allocation
where AllocatedMonth = DATEPART(MONTH, #maindate)
and AllocatedYear = DATEPART(YEAR, #maindate)
and Active = 1)
After insertion I would like somehow to update every other column (Grade1, Grade2,Absences...) from multiple other tables for each StudentID for the aforementioned Month and Year.
Any ideas?
This is what I usually perform batch update
UPDATE Monthly_Stats
SET
Monthly_Stats.GRADE1 = T1.Somedata + T2.Somedata + T3.Somedata
FROM
Monthly_Stats MS INNER JOIN TABLE_1 as T1
left join TABLE_2 as T2 on T1.StudentID = T2.StudentID and T1.Year = T2.Year and T1.Month = T2.Month
left join TABLE_3 as T3 on T1.StudentID = T3.StudentID and T1.Year = T3.Year and T1.Month = T3.Month
ON
MS.StudentID = T1.StudentID and MS.Year = T1.Year and MS.Month = T1.Month;
Be careful with the two left join. Depending on your database normalization, you may need more conditions in the ON clause to ensure the join output is as expected.
Hope it helps

SQL Server : Join If Between

I have 2 tables:
Query1: contains 3 columns, Due_Date, Received_Date, Diff
where Diff is the difference in the two dates in days
QueryHol with 2 columns, Date, Count
This has a list of dates and the count is set to 1 for everything. All these dates represent public holidays.
I want to be able to get the sum of QueryHol["Count"] if QueryHol["Date"] is between Query1["Due_Date"] and Query1["Received_Date"]
Result Wanted: a column joined onto Query1 to state how many public holidays fell into the date range so they can be subtracted from the Query1["Diff"] column to give a reflection of working days.
Because the 01-01-19 is a bank holiday i would want to minus that from the Diff to end up with results like below
Let me know if you require any more info.
Here's an option:
SELECT query1.due_date
, query1.received_date
, query1.diff
, queryhol.count
, COALESCE(query1.diff - queryhol.count, query1.diff) as DiffCount
FROM Query1
OUTER APPLY(
SELECT COUNT(*) AS count
FROM QueryHol
WHERE QueryHol.Date <= Query1.Received_Date
AND QueryHol.Date >= Query1.Due_Date
) AS queryhol
You may need to play around with the join condition - as it is assumes that the Received_Date is always later than the Due_Date which there is not enough data to know all of the use cases.
If I understand your problem, I think this is a possible solution:
select due_date,
receive_date,
diff,
(select sum(table2.count)
from table2
where table2.due_date between table1.due_date and table1.due_date) sum_holi,
table1.diff - (select sum(table2.count)
from table2
where table2.date between table1.due_date and table2.due_date) diff_holi
from table1
where [...] --here your conditions over table1.

T-SQL - Get last as-at date SUM(Quantity) was not negative

I am trying to find a way to get the last date by location and product a sum was positive. The only way i can think to do it is with a cursor, and if that's the case I may as well just do it in code. Before i go down that route, i was hoping someone may have a better idea?
Table:
Product, Date, Location, Quantity
The scenario is; I find the quantity by location and product at a particular date, if it is negative i need to get the sum and date when the group was last positive.
select
Product,
Location,
SUM(Quantity) Qty,
SUM(Value) Value
from
ProductTransactions PT
where
Date <= #AsAtDate
group by
Product,
Location
i am looking for the last date where the sum of the transactions previous to and including it are positive
Based on your revised question and your comment, here another solution I hope answers your question.
select Product, Location, max(Date) as Date
from (
select a.Product, a.Location, a.Date from ProductTransactions as a
join ProductTransactions as b
on a.Product = b.Product and a.Location = b.Location
where b.Date <= a.Date
group by a.Product, a.Location, a.Date
having sum(b.Value) >= 0
) as T
group by Product, Location
The subquery (table T) produces a list of {product, location, date} rows for which the sum of the values prior (and inclusive) is positive. From that set, we select the last date for each {product, location} pair.
This can be done in a set based way using windowed aggregates in order to construct the running total. Depending on the number of rows in the table this could be a bit slow but you can't really limit the time range going backwards as the last positive date is an unknown quantity.
I've used a CTE for convenience to construct the aggregated data set but converting that to a temp table should be faster. (CTEs get executed each time they are called whereas a temp table will only execute once.)
The basic theory is to construct the running totals for all of the previous days using the OVER clause to partition and order the SUM aggregates. This data set is then used and filtered to the expected date. When a row in that table has a quantity less than zero it is joined back to the aggregate data set for all previous days for that product and location where the quantity was greater than zero.
Since this may return multiple positive date rows the ROW_NUMBER() function is used to order the rows based on the date of the positive quantity day. This is done in descending order so that row number 1 is the most recent positive day. It isn't possible to use a simple MIN() here because the MIN([Date]) may not correspond to the MIN(Quantity).
WITH x AS (
SELECT [Date],
Product,
[Location],
SUM(Quantity) OVER (PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS Quantity,
SUM([Value]) OVER(PARTITION BY Product, [Location] ORDER BY [Date] ASC) AS [Value]
FROM ProductTransactions
WHERE [Date] <= #AsAtDate
)
SELECT [Date], Product, [Location], Quantity, [Value], Positive_date, Positive_date_quantity
FROM (
SELECT x1.[Date], x1.Product, x1.[Location], x1.Quantity, x1.[Value],
x2.[Date] AS Positive_date, x2.[Quantity] AS Positive_date_quantity,
ROW_NUMBER() OVER (PARTITION BY x1.Product, x1.[Location] ORDER BY x2.[Date] DESC) AS Positive_date_row
FROM x AS x1
LEFT JOIN x AS x2 ON x1.Product=x2.Product AND x1.[Location]=x2.[Location]
AND x2.[Date]<x1.[Date] AND x1.Quantity<0 AND x2.Quantity>0
WHERE x1.[Date] = #AsAtDate
) AS y
WHERE Positive_date_row=1
Do you mean that you want to get the last date of positive quantity come to positive in group?
For example, If you are using SQL Server 2012+:
In following scenario, when the date going to 01/03/2017 the summary of quantity come to 1(-10+5+6).
Is it possible the quantity of following date come to negative again?
;WITH tb(Product, Location,[Date],Quantity) AS(
SELECT 'A','B',CONVERT(DATETIME,'01/01/2017'),-10 UNION ALL
SELECT 'A','B','01/02/2017',5 UNION ALL
SELECT 'A','B','01/03/2017',6 UNION ALL
SELECT 'A','B','01/04/2017',2
)
SELECT t.Product,t.Location,SUM(t.Quantity) AS Qty,MIN(CASE WHEN t.CurrentSum>0 THEN t.Date ELSE NULL END ) AS LastPositiveDate
FROM (
SELECT *,SUM(tb.Quantity)OVER(ORDER BY [Date]) AS CurrentSum FROM tb
) AS t GROUP BY t.Product,t.Location
Product Location Qty LastPositiveDate
------- -------- ----------- -----------------------
A B 3 2017-01-03 00:00:00.000

SQL Grouping and Counting based on dates

Getting nowhere here and seems so simple.
Test data is:
declare #table table(SpellAdminsionDate datetime, SpellDischargeDate datetime, Pat_code varchar(10))
insert into #table (SpellAdminsionDate, SpellDischargeDate, Pat_code) values('2016-09-12 15:55:00:000','2016-09-19 20:20:00:000','HEY3052275')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-07 17:17:00:000','2016-09-17 18:40:00:000','HEY0810155')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-14 16:50:00:000','2016-09-17 18:01:00:000','HEY1059266')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-15 02:47:00:000','2016-09-15 17:28:00:000','HEY0742883')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-08-27 00:11:00:000','2016-09-14 12:49:00:000','HEY3050628')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-10 12:24:00:000','2016-09-13 20:00:00:000','HEY0912392')
insert into #table(SpellAdminsionDate, SpellDischargeDate, Pat_code) values ('2016-09-12 12:51:00:000','2016-09-13 19:55:00:000','HEY0908691')
Select * from #table`
Below is my simple code displaying the same thing:
SELECT c.SpellAdmissionDate,
c.SpellDischargeDate,
c.Pat_Code
FROM [CommDB].[dbo].[vwCivicaSLAM1617Live] c
WHERE c.Hrg_Code like 'VA%'
and c.Pat_Code like 'HEY%'
ORDER BY c.SpellDischargeDate desc
All I am after is a COUNT per day of active patients, for example take the 12/09/2016 on that date the result would be 5 (based on the test data) as the other 2 cam in after the 12th.
If it makes it easier I do have a date reference table called DATE_REFERENCE which has every date available to me.
Allowing for the possibility of having no patients on a day then you want to use your date reference table as the primary and left join to the patient information. You don't identify a column name so I just used [datecol].
SELECT
d.[datecol] the_date
, count(DISTINCT c.Pat_Code) num_patients
FROM DATE_REFERENCE d
LEFT JOIN [CommDB].[dbo].[vwCivicaSLAM1617Live] c
ON d.[datecol] BETWEEN c.SpellAdmissionDate AND c.SpellDischargeDate
AND c.Hrg_Code LIKE 'VA%'
AND c.Pat_Code LIKE 'HEY%'
GROUP BY
d.[datecol]
ORDER BY
d.[datecol] DESC
I suspect there may be more than this required, but without sample data and expected results it is difficult to know what you really need.
nb. I assume the date in that date_reference table is at midnight (00:00:00) or has a data type of date (without hours/minutes etc.)
Is this what you want?
SELECT dr.date,
(SELECT COUNT(*)
FROM [CommDB].[dbo].[vwCivicaSLAM1617Live] c
WHERE dr.date between c.SpellAdmissionDate and c.SpellDischargeDate) as cnt_per_day
FROM DATE_REFERENCE dr
Add the filters you want to the correlated query .
You can achieve this by joining to your date reference table and counting by a distinct of the patient reference
SELECT
dateRef
,COUNT(DISTINCT Pat_Code) AS PatCount
FROM #table t
RIGHT JOIN #date_reference
ON SpellAdminsionDate <= dateRef
AND SpellDischargeDate >= dateRef
GROUP BY dateRef;
Active count of patients per date can be achieved by (for temp data provided here)
SELECT DISTINCT CAST(T1.SPELLADMINSIONDATE AS DATE)
AdmissionDate,PATIENTS.TotalActive
FROM #TABLE T1
CROSS APPLY (SELECT COUNT(*)TotalActive FROM #TABLE T2 WHERE
CAST(T2.SPELLADMINSIONDATE AS DATE)<=CAST(T1.SPELLADMINSIONDATE AS DATE)) PATIENTS
I think you need to bring the DATE_REFERENCE table to the Query If it contains all the day dates that you need to count patients based on them, But here is a sample of how may you get the required result form this table only
Select DISTINCT c.SpellAdmissionDate, count(c.Pat_code) as PCounter
From [CommDB].[dbo].[vwCivicaSLAM1617Live] c
GROUP BY c.SpellAdmissionDate
ORDER BY c.SpellAdmissionDate desc

SQL Server 2012 Adding a computed column to a view from another table with different data types

SQL newbie here. I have been researching how to add a computed column to a view with data from another table but the haven't been able to get my desired result.
Consider this view:
CREATE VIEW [Portfolio].[MTDPNL]
AS
SELECT IssuerLS, Issuer, Ticker, SUM (GLPeriod) As [PNL], ReportDate
FROM Portfolio.DailyPortfolioIssuerLS
WHERE ReportDate
BETWEEN
(SELECT CONVERT(CHAR(10),DATEADD(dd,-(DAY(GetDate())-1),GetDate()), 120))
AND
(SELECT CONVERT(CHAR(10),(getdate()),120))
GROUP BY Issuer, Ticker, IssuerLS, ReportDate
Now consider this SELECT statement which returns a single integer value:
SELECT Nav AS [NavBOM]
FROM Portfolio.DailyStats ds
WHERE Date = (SELECT CONVERT(CHAR(10),DATEADD(dd,-(DAY(GetDate())-1),GetDate()), 120))
I want to add a column to my view that is PNL/NavBom. In other words, for every row in my view, I want to add a column that takes the number from the PNL column as the numerator and divides it by the the NavBOM number (so the denominator is the same for every row)
I have been testing with the following code:
SELECT IssuerLS, Issuer, Ticker, SUM (GLPeriod) As [PNL], ReportDate,
CAST(SUM (GLPeriod)/(SELECT Nav AS NavBOM
FROM Portfolio.DailyStats ds
WHERE Date = (SELECT CONVERT(CHAR(10),DATEADD(dd,-(DAY(GetDate())-1),GetDate()), 120)))
AS Decimal (7,4)) AS[%ofNAV]
FROM Portfolio.DailyPortfolioIssuerLS
WHERE ReportDate BETWEEN
(SELECT CONVERT(CHAR(10),DATEADD(dd,-(DAY(GetDate())-1),GetDate()),120))
AND
(SELECT CONVERT(CHAR(10),(getdate()),120))
GROUP BY Issuer, Ticker, IssuerLS, ReportDate
I'm not sure where the problem is but I'm getting a value of 0.0000 in the [%ofNAV] Column for every row. Both NavBOM and PNL are integer data types.. and since the NavBOM is going to be a much larger number compared to PNL, I need to convert it to decimal.
Also, the original view I created takes almost 4 seconds to execute. Probably because I have queries within my WHERE clause. Any suggestions to improve that will be greatly appreciated.
Thanks!
Here is a method using CTE to get your NavBOM and then using it in your view creation.
CREATE VIEW [Portfolio].[MTDPNL]
AS
WITH cteNav as(
SELECT Nav AS [NavBOM]
FROM Portfolio.DailyStats ds
WHERE Portfolio.Date = DATEADD(dd,-(DAY(GetDate())-1),GetDate()))
SELECT
IssuerLS,
Issuer,
Ticker,
SUM (GLPeriod) As [PNL],
SUM(CONVERT(DECIMAL(15,2),GLPeriod)) / CONVERT(DECIMAL(15,2),cteNav.[NavBOM]) as [%ofNAV]
ReportDate
FROM Portfolio.DailyPortfolioIssuerLS
JOIN cteNav on 1=1
WHERE ReportDate BETWEEN CONVERT(DATE,DATEADD(dd,-(DAY(GetDate())-1),GetDate())) AND getdate() --interesting way to get the first day of the month BTW
GROUP BY
Issuer, Ticker, IssuerLS, ReportDate, cteNav.[NavBOM]
Thanks scsimon, the code works with a little tweak:
WITH cteNav as(
SELECT Nav AS [NavBOM]
FROM Portfolio.DailyStats ds
WHERE ds.Date = CONVERT(DATE,DATEADD(dd,-(DAY(GetDate())-1),GetDate())))
SELECT IssuerLS, Issuer, Ticker, SUM (GLPeriod) As [PNL],
CAST(SUM(GLPeriod)As Decimal (12,2)) / CAST(cteNav.[NavBOM] As Decimal (12,2)) as [%ofNAV], ReportDate
FROM Portfolio.DailyPortfolioIssuerLS ls
JOIN cteNav on 1=1
WHERE ReportDate
BETWEEN
CONVERT(DATE,DATEADD(dd,-(DAY(GetDate())-1),GetDate()))
AND
CONVERT(CHAR(10),(getdate()),120)
GROUP BY Issuer, Ticker, IssuerLS, ReportDate, cteNav.[NavBOM]
GO

Resources