i have a Stored Procedure called "Patient Ledger Report" where i need to show the day to day transaction details and balance amount of the patients.i was providing you one sampled data from below code how the data was inserting into my temporary table in my sp.
create table #Patient_ledger (PATIENT_NAME varchar(250),PATIENT_NBR bigint,BILLNO varchar(250),BILLAMOUNT bigint,
PAID_AMOUNT bigint)
Insert into #Patient_ledger (Patient_name ,Patient_nbr ,billno ,billamount ,
paid_amount )
select 'ABC',1,'DUE_BILL_ABC_1',100,50
union all
select 'ABC',1,'DUE_BILL_ABC_2',160,90
UNION ALL
select 'ABC',1,'DEPOSIT_BILL_ABC',0,60
UNION ALL
select 'XYZ',2,'DEPOSIT_BILL_XYZ',0,70
UNION ALL
select 'XYZ',2,'DUE_BILL_XYZ_1',100,30
SELECT * FROM #Patient_ledger
Drop table #Patient_ledger
How i want to show the data in my report.
PATIENT_NUMBER BILLNO BILLAMOUNT PAID_AMOUNT BALANCE
1 DUE_BILL_ABC_1 100 50 50 --(100-50)
1 DUE_BILL_ABC_2 160 90 120 --(160-90 +50(Here 50 is prev balance amount of same patient))
1 DEPOSIT_BILL_ABC 0 40 80 ---( 120-40=80)
2 DEPOSIT_BILL_XYZ 0 70 0
2 DUE_BILL_XYZ_1 100 30 0 --Here Balance is zero because patient has deposited some
--amount before bill (70-100+30=0)
Note: Balance amount should deduct when deposits are paid by that particual patient.
I have tried like below it may help you
SELECT Patient_nbr,
billno,
billamount,
PAID_AMOUNT,
CASE
WHEN RNO > 1 THEN Sum(billamount - PAID_AMOUNT)
OVER(
PARTITION BY Patient_nbr
ORDER BY RNO)
ELSE Iif(( billamount - PAID_AMOUNT ) < 0, 0, billamount - PAID_AMOUNT)
END
FROM (SELECT *,
Row_number()
OVER(
PARTITION BY Patient_nbr
ORDER BY Patient_nbr) AS RNO
FROM #Patient_ledger) A
If you are able to put there also order discriminator, it could seems like this: (I consider also fact that there can be more DUE/DEPOSITS for one PATIENT_NBR)
IF OBJECT_ID('tempdb..#Patient_ledger') IS NOT NULL DROP TABLE #Patient_ledger
CREATE TABLE #Patient_ledger
(ID INT IDENTITY,
PATIENT_NAME varchar(250),
PATIENT_NBR bigint,
BILLNO varchar(250),
BILLAMOUNT bigint,
PAID_AMOUNT bigint)
Insert into #Patient_ledger (PATIENT_NAME ,PATIENT_NBR ,BILLNO ,BILLAMOUNT ,
PAID_AMOUNT )
select 'ABC',1,'DUE_BILL_ABC_1',100,50
union all
select 'ABC',1,'DUE_BILL_ABC_2',160,90
UNION ALL
select 'ABC',1,'DEPOSIT_BILL_ABC',0,40
UNION ALL
select 'XYZ',2,'DEPOSIT_BILL_XYZ',0,70
UNION ALL
select 'XYZ',2,'DUE_BILL_XYZ_1',100,30
;WITH CTE AS (
SELECT PATIENT_NBR,
BILLNO,
PAID_AMOUNT,
BILLAMOUNT,
BILLAMOUNT-PAID_AMOUNT AS BALANCE,
ROW_NUMBER() OVER (PARTITION BY PATIENT_NBR ORDER BY ID) AS RN
FROM #Patient_ledger)
SELECT a.PATIENT_NBR,
a.BILLNO,
a.BILLAMOUNT,
a.PAID_AMOUNT,
CASE WHEN ISNULL(LAG(a.BALANCE + ISNULL(x.ADDS,0)) OVER (PARTITION BY a.PATIENT_NBR ORDER BY a.RN),0) + a.BILLAMOUNT - a.PAID_AMOUNT < 0
THEN 0
ELSE a.BALANCE + ISNULL(x.ADDS,0)
END AS FINAL_BALANCE
FROM CTE a
CROSS APPLY (SELECT SUM(BALANCE) AS ADDS
FROM CTE f
WHERE f.PATIENT_NBR = a.PATIENT_NBR AND f.RN < a.RN) x
Try this and tell me if it work with other sample data too.
create table #Patient_ledger (PATIENT_NAME varchar(250),PATIENT_NBR bigint
,BILLNO varchar(250),BILLAMOUNT bigint,PAID_AMOUNT bigint)
Insert into #Patient_ledger (Patient_name ,Patient_nbr ,billno
,billamount ,paid_amount )
select 'ABC',1,'DUE_BILL_ABC_1',100,50
union all
select 'ABC',1,'DUE_BILL_ABC_2',160,90
UNION ALL
select 'ABC',1,'DEPOSIT_BILL_ABC',0,40
UNION ALL
select 'XYZ',2,'DEPOSIT_BILL_XYZ',0,70
UNION ALL
select 'XYZ',2,'DUE_BILL_XYZ_1',100,30
SELECT PATIENT_NBR PATIENT_NUMBER
,BILLNO
,BILLAMOUNT
,PAID_AMOUNT
,CASE
WHEN billamount = 0
AND lag((BILLAMOUNT - PAID_AMOUNT), 1, 0) OVER (
PARTITION BY PATIENT_NBR ORDER BY PATIENT_NBR
) = 0
THEN 0
ELSE SUM((BILLAMOUNT - PAID_AMOUNT)) OVER (
PARTITION BY PATIENT_NBR ORDER BY PATIENT_NBR ROWS UNBOUNDED PRECEDING
)
END Balance
FROM #Patient_ledger
Drop table #Patient_ledger
Related
Here's the output I'm hoping to achieve:
I'd like to start with the most recent ReportMonth working backwards and start a new partition every time there's a MonthsBetween > 2. I'd like to avoid loops if possible.
EDIT: Here's the create statement for the table
CREATE TABLE #temp (
MonthsBetween int,
ReportMonth date)
INSERT INTO #temp VALUES(0, '2019-12-01')
INSERT INTO #temp VALUES(1, '2019-11-01')
INSERT INTO #temp VALUES(1, '2019-10-01')
INSERT INTO #temp VALUES(3, '2019-07-01')
INSERT INTO #temp VALUES(1, '2019-06-01')
INSERT INTO #temp VALUES(3, '2019-03-01')
You may try with the following approach using LAG() and windowed SUM():
Statement (for groups based on difference between each two values for MonthsBetween > 2):
SELECT
MonthsBetween,
ReportMonth,
SUM(GroupID) OVER (ORDER BY ReportMonth DESC) AS PartitionID
FROM (
SELECT
*,
CASE
WHEN LAG(MonthsBetween) OVER (ORDER BY ReportMonth DESC) IS NULL THEN 1
WHEN MonthsBetween - LAG(MonthsBetween) OVER (ORDER BY ReportMonth DESC) >= 2 THEN 1
ELSE 0
END AS GroupId
FROM #temp
) cte
Statement (for groups based on value for MonthsBetween > 2):
SELECT
MonthsBetween,
ReportMonth,
SUM(CASE WHEN MonthsBetween > 2 THEN 1 ELSE 0 END) OVER (ORDER BY ReportMonth DESC) + 1 AS PartitionID
FROM #temp
Result:
MonthsBetween ReportMonth PartitionID
0 2019-12-01 1
1 2019-11-01 1
1 2019-10-01 1
3 2019-07-01 2
1 2019-06-01 2
3 2019-03-01 3
Looks like your MonthsBetween uses a Lag or Lead function already(or precalculated)
create table #temp (
MonthsBetween int null,
ReportMonth date null
)
insert into #temp (MonthsBetween,ReportMonth)
Values ('0','2019-12-01'),
('1','2019-11-01'),
('1','2019-10-01'),
('3','2019-07-01'),
('1','2019-06-01'),
('3','2019-03-01')
select t.*,
sum(case when MonthsBetween <= 2 then 0 else 1 end) over (order by t.[ReportMonth] DESC) +1 as [PartitionID]
from #temp t
I have this:
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
'{"EntityName":"account","Ids":["'+CAST(AccountId as varchar(50))+'"],"OwnerId":"0C01C994-1205-E511-988E-26EE4189191B"}' as [Parameters]
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
Within the parameter field is an id (0C01C994-1205-E511-988E-26EE4189191B). Is it possible it could sequentially assign a different id from a list for each row? There are 5 id's in total.
What i'm trying to get to is this result set equally split between the 5 different id's.
Thanks
You can add one more NEWID() in the sub query and handle in the SELECT as below:
SELECT id, [name], TypeId, OrgName, SystemUserId, StatusId, CreatedAt,
'{"EntityName":"account","Ids":["' + AccountId +'"],"OwnerId":"' + ParamId + '"}' as [Parameters]
FROM (
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
CAST(NEWID() AS VARCHAR (36)) as ParamId,
CAST(AccountId as varchar(50)) as AccountId
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) A
You can use something like the following. Basically, use a row number for both your IDs and your data table to update, then do a MOD (%) operation with the amount of ID's you want to assign, so your data table to update is split into N groups. Then use that group ID to assign each ID.
IF OBJECT_ID('tempdb..#IDsToAssign') IS NOT NULL
DROP TABLE #IDsToAssign
CREATE TABLE #IDsToAssign (
IDToAssign VARCHAR(100))
-- 3 IDs example
INSERT INTO #IDsToAssign (
IDToAssign)
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
DECLARE #AmountIDsToAssign INT = (SELECT COUNT(1) FROM #IDsToAssign)
IF OBJECT_ID('tempdb..#Account') IS NOT NULL
DROP TABLE #Account
CREATE TABLE #Account (
PrimaryKey INT PRIMARY KEY,
AssignedID VARCHAR(100))
-- 10 Rows example
INSERT INTO #Account (
PrimaryKey)
VALUES
(100),
(200),
(351),
(154),
(194),
(345),
(788),
(127),
(124),
(14)
;WITH DataRowNumber AS
(
SELECT
A.*,
RowNumber = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
#Account AS A
),
IDsRowNumbers AS
(
SELECT
D.IDToAssign,
RowNumber = ROW_NUMBER() OVER (ORDER BY D.IDToAssign)
FROM
#IDsToAssign AS D
),
NewIDAssignation AS
(
SELECT
R.*,
IDRowNumberAssignation = (R.RowNumber % #AmountIDsToAssign) + 1
FROM
DataRowNumber AS R
)
UPDATE A SET
AssignedID = R.IDToAssign
FROM
NewIDAssignation AS N
INNER JOIN IDsRowNumbers AS R ON N.IDRowNumberAssignation = R.RowNumber
INNER JOIN #Account AS A ON N.PrimaryKey = A.PrimaryKey
SELECT
*
FROM
#Account AS A
ORDER BY
A.AssignedID
/* Results:
PrimaryKey AssignedID
----------- ------------------------------------
124 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
194 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
351 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
788 2A58A573-EDCB-428E-A87A-6BFCED265A9C
200 2A58A573-EDCB-428E-A87A-6BFCED265A9C
127 2A58A573-EDCB-428E-A87A-6BFCED265A9C
14 2A58A573-EDCB-428E-A87A-6BFCED265A9C
100 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
154 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
345 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
*/
The ordering of the ROW_NUMBER() function will determine how ID's are assigned.
You could potentially do this by using the ROW_NUMBER() field in a subquery; for example:
SELECT NEWID() as id, 'OwnerReassign' as name, 1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId, GETDATE() as CreatedAt,
case B / ##ROWCOUNT
when 0 then '0C01C994-1205-E511-988E-26EE4189191B'
when 1 then '12345677-1205-E511-988E-26EE4189191B'
when 2 then '66666666-1205-E511-988E-26EE4189191B'
etc...
end
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY A.Id)
FROM Account A
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) AS B
If you want the system to pick those values then you could put then in their own temporary table, too.
I need to fill the range from 2017-04-01 to 2017-04-30 with the data from this table, knowing that the highest priority records should prevail over those with lower priorities
id startValidity endValidity priority
-------------------------------------------
1004 2017-04-03 2017-04-30 1
1005 2017-04-10 2017-04-22 2
1010 2017-04-19 2017-04-23 3
1006 2017-04-24 2017-04-28 2
1008 2017-04-26 2017-04-28 3
In practice I would need to get a result like this:
id startValidity endValidity priority
--------------------------------------------
1004 2017-04-03 2017-04-09 1
1005 2017-04-10 2017-04-18 2
1010 2017-04-19 2017-04-23 3
1006 2017-04-24 2017-04-25 2
1008 2017-04-26 2017-04-28 3
1004 2017-04-29 2017-04-30 1
can't think of anything elegant or more efficient solution right now . . .
-- Sample Table
declare #tbl table
(
id int,
startValidity date,
endValidty date,
priority int
)
-- Sample Data
insert into #tbl select 1004, '2017-04-03', '2017-04-30', 1
insert into #tbl select 1005, '2017-04-10', '2017-04-22', 2
insert into #tbl select 1010, '2017-04-19', '2017-04-23', 3
insert into #tbl select 1006, '2017-04-24', '2017-04-28', 2
insert into #tbl select 1008, '2017-04-26', '2017-04-28', 3
-- Query
; with
date_range as -- find the min and max date for generating list of dates
(
select start_date = min(startValidity), end_date = max(endValidty)
from #tbl
),
dates as -- gen the list of dates using recursive CTE
(
select rn = 1, date = start_date
from date_range
union all
select rn = rn + 1, date = dateadd(day, 1, d.date)
from dates d
where d.date < (select end_date from date_range)
),
cte as -- for each date, get the ID based on priority
(
select *, grp = row_number() over(order by id) - rn
from dates d
outer apply
(
select top 1 x.id, x.priority
from #tbl x
where x.startValidity <= d.date
and x.endValidty >= d.date
order by x.priority desc
) t
)
-- final result
select id, startValidity = min(date), endValidty = max(date), priority
from cte
group by grp, id, priority
order by startValidity
I do not understand the purpose of Calendar CTE or table.
So I am not using any REcursive CTE or calendar.
May be I hvn't understood the requirement completly.
Try this with diff sample data,
declare #tbl table
(
id int,
startValidity date,
endValidty date,
priority int
)
-- Sample Data
insert into #tbl select 1004, '2017-04-03', '2017-04-30', 1
insert into #tbl select 1005, '2017-04-10', '2017-04-22', 2
insert into #tbl select 1010, '2017-04-19', '2017-04-23', 3
insert into #tbl select 1006, '2017-04-24', '2017-04-28', 2
insert into #tbl select 1008, '2017-04-26', '2017-04-28', 3
;With CTE as
(
select * ,ROW_NUMBER()over(order by startValidity)rn
from #tbl
)
,CTE1 as
(
select c.id,c.startvalidity,isnull(dateadd(day,-1, c1.startvalidity)
,c.endValidty) Endvalidity
,c.[priority],c.rn
from cte c
left join cte c1
on c.rn+1=c1.rn
)
select id,startvalidity,Endvalidity,priority from cte1
union ALL
select id,startvalidity,Endvalidity,priority from
(
select top 1 id,ca.startvalidity,ca.Endvalidity,priority from cte1
cross apply(
select top 1
dateadd(day,1,endvalidity) startvalidity
,dateadd(day,-1,dateadd(month, datediff(month,0,endvalidity)+1,0)) Endvalidity
from cte1
order by rn desc)CA
order by priority
)t4
--order by startvalidity --if req
I have a table as
CREATE TABLE #FinalRates
(
id int primary key identity(1,1),
RateDesc nvarchar(50),
Amt decimal(18,2)
)
insert into #FinalRates values('100',200)
insert into #FinalRates values('100',300)
insert into #FinalRates values('50-80',100)
insert into #FinalRates values('50-80',300)
insert into #FinalRates values('30-50',500)
insert into #FinalRates values('30-50',250)
Looking for an output as
RateDesc Amount1 Amount2
100 200 300
50-80 100 300
30-50 500 250
I have done this as
;with cte as(
select
RateDesc
,Amounts=
STUFF((Select ','+ cast(cast(Amt as int) as varchar(10))
from #FinalRates T1
where T1.RateDesc=T2.RateDesc
FOR XML PATH('')),1,1,'')
from #FinalRates T2
group by T2.RateDesc
)
select
RateDesc,
Amount1 = PARSENAME(REPLACE(Amounts,',','.'),2),
Amount2 = PARSENAME(REPLACE(Amounts,',','.'),1)
From Cte
Drop table #FinalRates
Can the same be done using PIVOT?
That's so complicated. How about this?
select ratedesc,
max(case when seqnum = 1 then amt end) as Amount1,
max(case when seqnum = 2 then amt end) as Amount2
from (select ft.*,
row_number() over (partition by ratedesc order by id) as seqnum
from #finalrates fr
) fr
group by ratedesc;
You could use a similar approach using pivot but conditional aggregation often performs better.
Plus, if you know you have no holes in id, you can do:
select ratedesc,
max(case when id % 2 = 1 then amt end) as Amount1,
max(case when id % 2 = 0 then amt end) as Amount2
from #finalrates fr
group by ratedesc;
Using PIVOT,
Assuming you have 2 Amt for each RateDesc.
Select RateDesc, [Amount1], [Amount2] From
(
Select RateDesc, Amt
, 'Amount' + cast(row_number() over (partition by RateDesc order by Amt) as varchar(5)) RowVal
from #FinalRates
) x
PIVOT
(
MAX(Amt) For RowVal in ([Amount1], [Amount2])
) p
I have exhausted my search for a solution and would like to post my question to see if a solution exists.
I need to write a report to show the debits and credits per branch. The report needs also show if branches have had no DBs or CRs.
For simplicity I have scaled down my tables to try highlight my issue.
My first table holds my Branch Data
BranchNo BranchName
1 Main
2 Mgorogoro
3 Arusha
My second table holds all Debit Transactions
txid Narrative Amount Date BranchNo
1 Test 1 50.00 2014/11/26 1
2 Test 2 20.00 2014/11/27 3
I've written a SQL statement that gives me the results I need:-
DECLARE #get_Dates CURSOR;
DECLARE #Date VarChar(10);
DECLARE #tbl TABLE
(
DebitOutCount int,
BranchCode VarChar(250),
TxDate VarChar(10)
)
--DECLARE #tbl TABLE(Idx1 VarChar(50), Idx8 VarChar(50), Idx3 VarChar(50))
SET #get_Dates = CURSOR FOR
Select Debits_OUT.Date FROM Debits_OUT GROUP BY Debits_OUT.Date ORDER BY Debits_OUT.Date
OPEN #get_Dates;
FETCH NEXT FROM #get_Dates into #Date;
WHILE (##FETCH_STATUS = 0)
BEGIN
--INSERT INTO #tbl SELECT Idx1, Idx8, Idx3 FROM SessionDailyControl WHERE Idx1 = #sessionId
INSERT INTO #tbl
SELECT
(SELECT ISNULL(SUM(DB_OUT.Amount), 0) FROM Debits_OUT AS DB_OUT WHERE B.BranchNo = DB_OUT.BranchNo AND DB_OUT.Date = #Date) AS DebitOutValue,
CAST(B.BranchNo As VarChar(10)) + ' ' + B.BranchName As [Branch Names],
#Date
From exBranches As B
FETCH NEXT FROM #get_Dates into #Date
END
CLOSE #get_Dates
DEALLOCATE #get_Dates
SELECT * FROM #tbl
The result is in the format that I need:-
DebitOutCount BranchCode TxDate
50 1 Main 2014/11/26
0 2 Mgorogoro 2014/11/26
0 3 Arusha 2014/11/26
0 1 Main 2014/11/27
0 2 Mgorogoro 2014/11/27
20 3 Arusha 2014/11/27
However, the report tools and Views cannot work with the above. I have tried Left Joins - but the problem is the result set will not keep the branch numbers for dates where there were zero transactions. For Example:-
SELECT
ISNULL(SUM(B.Amount), 0),
CAST(A.BranchNo As VarChar(10)) + ' ' + A.BranchName As [Branch Names],
B.Date
From exBranches As A
LEFT JOIN Debits_OUT AS B ON A.BranchNo = B.BranchNo
GROUP BY B.Date, A.BranchNo, A.BranchName
ORDER BY B.Date, A.BranchNo, A.BranchName
Returns:-
DB_OUT Branch Names Date
0.00 2 Mgorogoro NULL
50.00 1 Main 2014/11/26
20.00 3 Arusha 2014/11/27
In all the JOIN combinations that I try, I cannot get the branches to show ALL the branches for each date that is in the debits table.
Is there a fundamental concept that I have completely missed? I need have a query that can be run in a view that returns the same data as the cursor statement. Is this possible?
The idea is to generate possible combinations of Branches and dates first:
create table exBranches(
BranchNo int,
BranchName varchar(20)
)
create table Debits_OUT(
txId int,
Narrative varchar(20),
Amount decimal (6,2),
[Date] date,
BranchNo int
)
insert into exBranches values (1, 'Main'), (2, 'Mgorogoro'), (3, 'Arusha')
insert into Debits_OUT values (1, 'Test 1', 50.00, '20141126', 1), (2, 'Test 2', 20.00, '20141127', 3);
with BranchDate as(
select
b.BranchNo,
b.BranchName,
d.Date
from exBranches b
cross join (
select distinct [Date] from Debits_OUT
)d
)
select
isnull(DebitOutCount,0),
cast(b.BranchNo as varchar(10)) + ' ' + b.BranchName as BranchName,
b.Date
from BranchDate b
left join (
select
branchNo,
[Date],
sum(Amount) as DebitOutCount
from Debits_OUT
group by
BranchNo, [Date]
)d
on d.BranchNo = b.BranchNo
and d.Date = b.Date
order by b.date, b.BranchNo asc
drop table exBranches
drop table Debits_OUT
Try This it's works.....
select BranchName,amount,date1,BranchNo into #temp from exBranches
cross join (select distinct date1,amount from Debits_OUT)a
select isnull(t.amount,0),a.BranchName,a.date1 from #temp a
left join Debits_OUT t on t.BNo=a.BranchNo and a.date1=t.date1
order by date1
view here..
http://sqlfiddle.com/#!3/ad815/1