SQL Subquery with SUMs - tsql

I have the following query from a Ledger table in my accounting database:
select lmatter
, ltradat
, llcode
, lamount
, lperiod
from ledger
where lmatter = '1234-ABCD'
and lzero <> 'R'
order by lperiod
The results are:
From this, I want to know if it is possible to create a final result of:
The way I got these #'s is as follows:
We are working under the assumption that today is December 1st, 2015 and we are billing for November 2015 data. Which makes the lperiod we're working with as "current" equal to '1115'
Matter ID is a distinct lmatter
Prev. Bal is SUM(FEES + HCOST) - SUM(PAY) where lperiod <> '1115'
Payments is SUM(PAY) where lperiod = '1115'
Current Charges is SUM(FEES + HCOST) where lperiod = '1115'
Amount Due is Prev.Bal - Payments + Current Charges
Is this possible under one query with use of sub-queries, or possibly even the use of a couple temp tables?

use case
you can build up more advanced logic
for Amount Due I would have nested query
select *, prev - pay + current as 'DUE'
from
(
select lmatter
, sum(case when lperiod <> '1115' and llcode in ('FEES', 'HCOST') then lamount
when lperiod <> '1115' and llcode = 'PAY' then -lamount
end) as 'prev'
, sum(case when lperiod = '1115' and llcode = 'PAY' then lamount
end) as 'payment'
, sum(case when lperiod = '1115' and llcode in ('FEES', 'HCOST') then lamount
end) as 'current'
from ledger
where lmatter = '1234-ABCD'
and lzero <> 'R'
group by by lmatter
) as tt

Related

filling missing rows with currency

I have the following table (with different currencies):
date
currency
ex_rate
30/11/2020 00.00
USD
0.8347245409015025
27/11/2020 00.00
USD
0.8387854386847845
26/11/2020 00.00
USD
0.84033613445378152
As you can see, there is some missing data for two dates. I would like to fill it with the previous available date, so it would be like this:
date
currency
ex_rate
30/11/2020 00.00
USD
0.8347245409015025
29/11/2020 00.00
USD
0.8387854386847845
28/11/2020 00.00
USD
0.8387854386847845
27/11/2020 00.00
USD
0.8387854386847845
26/11/2020 00.00
USD
0.84033613445378152
Or redirect me to a question of the same kind
You can use generate_series to build the series of dates from the earlierst and later values available in the table, then bring the corresponding rows with a lateral join:
select d.dt, 'USD', t.ex_rate
from (
select generate_series(min(date), max(date), interval '1 day') as dt
from mytable
where currency = 'USD'
) d
cross join lateral (
select t.*
from mytable t
where currency = 'USD' and t.date <= d.dt
order by t.date desc limit 1
) t
I wonder whether a left join on date equality and then some window function technique to build groups of records might be more efficient:
select dt, 'USD', max(ex_rate) over(partition by grp) as ex_rate
from (
select d.*, t.*,
count(t.date) over(order by d.dt) as grp
from (
select generate_series(min(date), max(date), interval '1 day') as dt
from mytable
where currency = 'USD'
) d
left join mytable t on currency = 'USD' and t.date = d.dt
) t
Note that this can easily be generalized to handle all currencies at once:
select dt, currency, max(ex_rate) over(partition by currency, grp) as ex_rate
from (
select d.dt, c.currency, t.ex_rate,
count(t.date) over(partition by c.currency order by d.dt) as grp
from (select distinct currency from mytable) c
cross join (
select generate_series(min(date), max(date), interval '1 day') as dt
from mytable
) d
left join mytable t on t.currency = c.currency and t.date = d.dt
) t

TSQL - Replace Cursor

I found in our database a cursor statement and I would like to replace it.
Declare #max_date datetime
Select #max_date = max(finished) From Payments
Declare #begin_date datetime = '2015-02-01'
Declare #end_of_last_month datetime
While #begin_date <= #max_date
Begin
SELECT #end_of_last_month = CAST(DATEADD(DAY, -1 , DATEFROMPARTS(YEAR(#begin_date),MONTH(#begin_date),1)) AS DATE) --AS end_of_last_month
Insert Into #table(Customer, ArticleTypeID, ArticleType, end_of_month, month, year)
Select Count(distinct (customerId)), prod.ArticleTypeID, at.ArticleType, #end_of_last_month, datepart(month, #end_of_last_month), datepart(year, #end_of_last_month)
From Customer cust
Inner join Payments pay ON pay.member_id = m.member_id
Inner Join Products prod ON prod.product_id = pay.product_id
Inner Join ArticleType at ON at.ArticleTypeID = prod.ArticleTypeID
Where #end_of_last_month between begin_date and expire_date
and completed = 1
Group by prod.ArticleTypeID, at.ArticleType
order by prod.ArticleTypeID, at.ArticleType
Set #begin_date = DATEADD(month, 1, #begin_date)
End
It groups all User per Month where the begin- and expire date in the actual Cursormonth.
Notes:
The user has different payment types, for e.g. 1 Month, 6 Month and so on.
Is it possible to rewrite the code - my problem is only the identification at the where clause (#end_of_last_month between begin_date and expire_date)
How can I handle this with joins or cte's?
What you need first, if not already is a numbers table
Using said Numbers table you can create a dynamic list of dates for "end_of_Last_Month" like so
;WITH ctexAllDates
AS (
SELECT end_of_last_month = DATEADD(DAY, -1, DATEADD(MONTH, N.N -1, #begin_date))
FROM
dbo.Numbers N
WHERE
N.N <= DATEDIFF(MONTH, #begin_date, #max_date) + 1
)
select * FROM ctexAllDates
Then combine with your query like so
;WITH ctexAllDates
AS (
SELECT end_of_last_month = DATEADD(DAY, -1, DATEADD(MONTH, N.N -1, #begin_date))
FROM
dbo.Numbers N
WHERE
N.N <= DATEDIFF(MONTH, #begin_date, #max_date) + 1
)
INSERT INTO #table
(
Customer
, ArticleTypeID
, ArticleType
, end_of_month
, month
, year
)
SELECT
COUNT(DISTINCT (customerId))
, prod.ArticleTypeID
, at.ArticleType
, A.end_of_last_month
, DATEPART(MONTH, A.end_of_last_month)
, DATEPART(YEAR, A.end_of_last_month)
FROM
Customer cust
INNER JOIN Payments pay ON pay.member_id = m.member_id
INNER JOIN Products prod ON prod.product_id = pay.product_id
INNER JOIN ArticleType at ON at.ArticleTypeID = prod.ArticleTypeID
LEFT JOIN ctexAllDates A ON A.end_of_last_month BETWEEN begin_date AND expire_date
WHERE completed = 1
GROUP BY
prod.ArticleTypeID
, at.ArticleType
, A.end_of_last_month
ORDER BY
prod.ArticleTypeID
, at.ArticleType;

T-SQL Pivot query

I have a table that contains a laserform name and a date e.g.
My table is called myBWB_LaserForms
laserform editwhen
LRAP1.lpw 2009-09-03 16:06:00.000
CON29R.lpw 2009-10-29 12:35:00.000
UN1.lpw 2010-03-02 11:38:00.000
UN244.lpw 2012-04-19 12:14:00.000
LT3.lpw 2013-01-02 11:00:00.000
LRAP1.lpw 2007-12-10 15:34:00.000
What I am trying to produce is a report which has the name of the laserform down the left and years across the top e.g. 2004, 2005, 2006 and then each intersecting cell would have a count of the number of records for that form name for that particular year.
Normally I would do something like
select l.laserform,
(select count(*) from mybwb_laserforms l1 where l1.laserform=l.laserform and year(editwhen)=2004) AS [2004],
(select count(*) from mybwb_laserforms l2 where l2.laserform=l.laserform and year(editwhen)=2005) AS [2005],
(select count(*) from mybwb_laserforms l3 where l3.laserform=l.laserform and year(editwhen)=2006) AS [2006],
(select count(*) from mybwb_laserforms l4 where l4.laserform=l.laserform and year(editwhen)=2007) AS [2007]
from mybwb_laserforms l
group by l.laserform
order by l.laserform
However I realise that this is pretty horrible SQL and I should be much more intelligent about it.
I've been trying to learn how to use the pivot function but am struggling to get my head around it.
Also most examples I have found seem to have the columns of the query hard coded - it would be good if the pivot could be dynamic and get the unique year(editwhen) values so I don't have to modify the query next year and the year after that.
Just sum it up.
select laserform,
sum(case when year(editwhen)=2004 then 1 else 0 end) AS [2004],
sum(case when year(editwhen)=2005 then 1 else 0 end) AS [2005],
sum(case when year(editwhen)=2006 then 1 else 0 end) AS [2006],
sum(case when year(editwhen)=2007 then 1 else 0 end) AS [2007]
from mybwb_laserforms
where year(editwhen) between 2004 and 2007
group by laserform
order by laserform
Or dynamic
declare #Year int = year(GetDate());
declare #Years TABLE ([year] varchar(4));
insert into #Years values (#Year-12),(#Year-11),(#Year-10),(#Year-9);
declare #SQLSums varchar(max) = '';
select #SQLSums = #SQLSums +char(13)+', sum(case when year(editwhen)='+[year]+' then 1 else 0 end) AS ['+[year]+']'
from #Years;
DECLARE #SQL nvarchar(max) = 'select laserform'+ #SQLSums +'
from mybwb_laserforms
group by laserform
where year(editwhen) between '+cast(#Year-12 as varchar)+' and '+cast(#Year-9 as varchar)+'
order by laserform';
--select #SQL;
exec (#SQL);

SUM OVER PARTITION to calculate running total

I am trying to modify my query to include a running total for each county in my report. Below is my working query with an attempt to use SUM OVER PARTITION commented out:
SELECT DATEPART(MONTH, r.received_date) AS [MonthID] ,
DATENAME(MONTH, r.received_date) AS [Month] ,
o.name AS [CountyName] ,
rsc.description AS [Filing] ,
COUNT(r.id) AS [Request_Total] ,
CAST (AVG(CAST (DATEDIFF(HOUR, received_date, completion_date) AS DECIMAL(8,2))) / 24 AS DECIMAL(8,2)) AS [Total_Time_Days]
--SUM(r.id) OVER (PARTITION BY o.name) AS [TotalFilings]
FROM dbo.requests AS [r]
INNER JOIN dbo.organizations AS [o] ON o.id = r.submitted_to_organiztion_id
INNER JOIN dbo.request_status_codes AS [rsc] ON rsc.code = r.request_status_code
WHERE r.submitted_to_organiztion_id < 68
AND r.request_type_code = 1
AND CAST(r.received_date AS DATE) >= '01/01/2016'
AND CAST(r.received_date AS DATE) <= '06/30/2016'
AND o.name = 'Alachua'
GROUP BY DATENAME(MONTH, r.received_date) ,
DATEPART(MONTH, r.received_date) ,
o.name ,
rsc.description
ORDER BY DATEPART(MONTH, r.received_date) ,
CountyName ,
Filing;
And the results look correct:
Perhaps I am misusing the SUM PARTITION BYbut my end goal is to add an additional column that will sum the filing types for each county by month.
For example, the additional column for the month of January should be 13,654 while February should be 14,238 and so on.
Could I get some advice on how to get this query working correctly? Thanks,
Not sure this is the best way or more efficient, but I was able to create a sub-query to obtain the results I wanted. I do believe a CTE or use of a Windows function would be better, but I haven't been able to get it to work. Here is my query however:
SELECT X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing] ,
X.[Avg_Time_Days] ,
SUM(X.Request_Total) AS [Total_Requests]
FROM ( SELECT DATEPART(MONTH, r.received_date) AS [MonthID] ,
DATENAME(MONTH, r.received_date) AS [Month] ,
o.name AS [CountyName] ,
rsc.description AS [Filing] ,
COUNT(r.id) AS [Request_Total] ,
CAST (AVG(CAST (DATEDIFF(HOUR, received_date,
completion_date) AS DECIMAL(8, 2)))
/ 24 AS DECIMAL(8, 2)) AS [Avg_Time_Days]
--, SUM(r.id) OVER (PARTITION BY o.name, rsc.description) AS [TotalFilings]
FROM dbo.requests AS [r]
INNER JOIN dbo.organizations AS [o] ON o.id = r.submitted_to_organiztion_id
INNER JOIN dbo.request_status_codes AS [rsc] ON rsc.code = r.request_status_code
WHERE r.submitted_to_organiztion_id < 68
AND r.request_type_code = 1
AND CAST(r.received_date AS DATE) >= '01/01/2016'
AND CAST(r.received_date AS DATE) <= '06/30/2016'
--AND o.name = 'Alachua'
GROUP BY DATENAME(MONTH, r.received_date) ,
DATEPART(MONTH, r.received_date) ,
o.name ,
rsc.description
--, r.id
--ORDER BY DATEPART(MONTH, r.received_date) ,
-- CountyName ,
-- Filing
) AS X
GROUP BY X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing] ,
X.[Avg_Time_Days]
ORDER BY X.[MonthID] ,
X.[Month] ,
X.[CountyName] ,
X.[Filing];

Extract data from Windows Server Update Services (WSUS) using SQL query

I have never used WSUS, so I really dont know how to get right data. The goal is to have a table with three columns: Computer Group, Computer Name, # of needed updates
I found out here: Microsoft Developer Network
that WSUS uses SQL server to store data that I need and I can connect to it via \.\pipe\MSSQL$MICROSOFT##SSEE\sql\query. I prefer this method, not PowerShell or something else, because finally I need this data in other sqlserver.
Could somebody please help me with SQL query that will extract needed info? I cant see anything familiar in database or PUBLIC_VIEWs. Many thanks.
Using SUSDB here is a minimal version with columns you requested:
Select tg.Name as ComputerGroup, ct.FullDomainName as ComputerName, Count(*) As Needed
From tbComputerTarget AS ct
Join tbComputerTargetDetail ctd on ctd.TargetId = ct.TargetId
Join tbTargetInTargetGroup tgct on tgct.TargetId = ct.TargetId
Join tbTargetGroup tg on tg.TargetGroupId = tgct.TargetGroupId
Join tbUpdateStatusPerComputer as s on ct.TargetID = s.TargetID And SummarizationState In(2,3,6)
Group By tg.Name, ct.FullDomainName;
Using SUSDB, here is a summary by computer with more columns that you requested:
With cteUpdateInstallationInfo As(
SELECT u.UpdateID
, ct.ComputerID
, (CASE WHEN usc.SummarizationState IS NULL OR usc.SummarizationState = 1 THEN (CASE WHEN ISNULL(u.LastUndeclinedTime, u.ImportedTime) < ct.EffectiveLastDetectionTime THEN 1 ELSE 0 END) ELSE usc.SummarizationState END) AS State
FROM dbo.tbUpdate AS u
JOIN dbo.tbRevision AS r ON u.LocalUpdateID = r.LocalUpdateID And r.IsLatestRevision = 1
JOIN dbo.tbProperty AS p ON r.RevisionID = p.RevisionID And p.ExplicitlyDeployable = 1
CROSS JOIN dbo.tbComputerTarget AS ct
LEFT JOIN dbo.tbUpdateStatusPerComputer AS usc ON u.LocalUpdateID = usc.LocalUpdateID AND ct.TargetID = usc.TargetID
WHERE u.IsHidden = 0
), Summary as(
Select ComputerId
, Count(*) as Total
, Sum(case When State = 0 Then 1 else 0 end) as NoStatus
, Sum(case When State = 1 Then 1 else 0 end) as NotApp
, Sum(case When State In(2,3,6) Then 1 else 0 end) as Needed
, Sum(case When State = 4 Then 1 else 0 end) as Installed
, Sum(case When State = 5 Then 1 else 0 end) as Failed
From cteUpdateInstallationInfo
Group by ComputerId
), TimeZone as (
Select DATEDIFF(mi, GetUtcDate(), GetDate()) as TimeZoneMinutes
)
Select ct.ComputerId
, ct.FullDomainName
, ct.IPAddress
, tg.Name as TargetGroupName
, Total, NoStatus, NotApp, Needed, Failed, Installed
, Dateadd(mi, TimeZoneMinutes, ct.EffectiveLastDetectionTime) As LastDetectLocalTime
, Dateadd(mi, TimeZoneMinutes, ct.LastReportedStatusTime) as LastReportLocalTime
, Dateadd(mi, TimeZoneMinutes, ct.LastSyncTime) As LastContactLocalTime
, LastSyncResult
, Dateadd(mi, TimeZoneMinutes, ct.LastReportedRebootTime) As LastRebootLocalTime
, Dateadd(mi, TimeZoneMinutes, ct.LastInventoryTime) As LastInventoryLocalTime
, Dateadd(mi, TimeZoneMinutes, ct.LastNameChangeTime) As LastNameChangeLocalTime
--, IsRegistered
--, OSMajorVersion, OSMinorVersion, OSBuildNumber, OSServicePackMajorNumber,OSServicePackMinorNumber
, OSLocale
, ComputerMake
, ComputerModel
, BiosVersion
, BiosName
, BiosReleaseDate
, ProcessorArchitecture
--, LastStatusRollupTime LastReceivedStatusRollupNumber LastSentStatusRollupNumber
--, SamplingValue, CreatedTime, SuiteMask, OldProductType, NewProductType, SystemMetrics
, ClientVersion
--, TargetGroupMembershipChanged
, OSFamily
, OSDescription
, OEM
, DeviceType
, FirmwareVersion
, MobileOperator
From Summary as s
Join TimeZone as tz on 1=1
JOIN dbo.tbComputerTarget AS ct on ct.ComputerID = s.ComputerId
Join tbComputerTargetDetail ctd on ctd.TargetId = ct.TargetId
Join tbTargetInTargetGroup tgct on tgct.TargetId = ct.TargetId
Join tbTargetGroup tg on tg.TargetGroupId = tgct.TargetGroupId
Also as a bonus here is a summary by update:
With Summary as(
Select UpdateId
, Count(*) as Total
, Sum(case When State = 0 Then 1 else 0 end) as NoStatus
, Sum(case When State = 1 Then 1 else 0 end) as NotApp
, Sum(case When State In(2,3,6) Then 1 else 0 end) as Needed
, Sum(case When State = 4 Then 1 else 0 end) as Installed
, Sum(case When State = 5 Then 1 else 0 end) as Failed
From PUBLIC_VIEWS.vUpdateInstallationInfo
Group BY UpdateId
), TimeZone as (
Select DATEDIFF(mi, GetUtcDate(), GetDate()) as TimeZoneMinutes
)
Select s.UpdateId
, u.IsDeclined
, u.PublicationState
, u.DefaultTitle as Title
, u.KnowledgebaseArticle as KBArticle
, Total, NoStatus, NotApp, Needed, Failed, Installed
, Dateadd(mi, TimeZoneMinutes, u.CreationDate) As ReleaseLocalTime
, Dateadd(mi, TimeZoneMinutes, u.ArrivalDate) As ArrivalLocalTime
From summary as s
Join TimeZone as tz on 1=1
Join PUBLIC_VIEWS.vUpdate as u on u.UpdateID = s.UpdateId