SQL Server Aggregation Using Partitioning - sql-server

I have been trouble writing a SQL query to roll up multiple balances based on a similar ID group and display the balance against products with a flag of N. I imagine I need to use a partition function or a max funcction to do this.
The desired results are in the table below underneath the sample dataset. Would anyone have a fix for this available?
Would anyone know logic to help for this? Basically I need to group everything by the ID and where there is a flag of N roll the balances up to that record, if there is no record with a flag of N we just aggregate by pdct_Type_C.
SELECT
Client,
SUM(Limit) Limit,
SUM(Balance) Balance,
SUM(Exposure) Exposure,
MAX(CASE WHEN Flag = 'N' THEN Pdct_type_c ELSE NULL END) Pdct_type_c,
ID
FROM Table
GROUP BY Client, ID
SAMPLE DATASET
Client | Limit | Balance | Exposure | Pdct_type_c | Flag | ID
--------------------------------------------------------------------------------
John | 60,000,000.00| - | 5,000,000| DERIV | N | 2
John | - | 1,000,000.00 | - | FX | y | 2
John | - | 2,000,000.00 | - | IC | y | 2
John | 1,000,000.00 | 3,000,000.00 | - | DCO | y | 3
John | 1,000,000.00 | 3,000,000.00 | - | DCO | y | 3
CURRENT RESULTS
Client | Limit | Balance | Exposure | Pdct_type_c | Flag | ID
--------------------------------------------------------------------------------
John | 60,000,000.00| 3,000,000.00 | 5,000,000| DERIV | N | 2
John | 2,000,000.00 | 6,000,000.00 | - | NULL | Y | 3
DESIRED RESULTS
Client | Limit | Balance | Exposure | Pdct_type_c | Flag | ID
--------------------------------------------------------------------------------
John | 60,000,000.00| 3,000,000.00 | 5,000,000| DERIV | N | 2
John | 2,000,000.00 | 6,000,000.00 | - | DCO | Y | 3

It is entirely possible that this is doable with a windowing function. However here is the old fashioned way of doing it
This shows us just the records that have an N entry
SELECT
ID,
MIN(Pdct_type_c) Pdct_type_c
FROM Table
WHERE Flag = 'N'
GROUP BY ID
This outer joins to it to decide what to group on
SELECT
T.Client,
SUM(T.Limit) Limit,
SUM(T.Balance) Balance,
SUM(T.Exposure) Exposure,
ISNULL(N.Pdct_type_c, T.Pdct_type_c) Pdct_type_c
CASE WHEN N.Pdct_type_c IS NULL THEN T.Flag ELSE 'N' END Flag,
T.ID
FROM Table T
LEFT OUTER JOIN
(
SELECT
ID,
MIN(Pdct_type_c) Pdct_type_c
FROM Table
WHERE Flag = 'N'
GROUP BY ID
) N
ON T.ID = N.ID
GROUP BY T.Client, T.ID,
ISNULL(N.Pdct_type_c, T.Pdct_type_c),
CASE WHEN N.Pdct_type_c IS NULL THEN T.Flag ELSE 'N' END

Related

SQL Server Case Statement- Deriving one column from two others when grouping by transactions

I am looking to apply make a flag based on the below scenario. The source table and desired results I am after are in the tables below.
When SRCE_SYST_FLAG isn't null (and = Y), then create a new flag (DERIVED_FLAG) using the value in the SRCE_SYST_FLAG column, else use the GENERIC_FLAG column.
However if another record in the same TRAN_ID group has the SRCE_SYST_FLAG column populated, set the DERIVED_FLAG to the other SRCE_SYST_F. This is the case in TRAN_ID = 2222 below.
Would anyone know how to do this using a case statement? I can make a generic one such as the one below, but this doesn't use the SRCE_SYST_FLAG for the records that are NULL but the SRCE_SYST_FLAG is populated for another record in that tran_id group :
CASE
WHEN SRCE_SYST_Flag = 'Y'
THEN SRCE_SYST_FLAG
ELSE GENERIC_FLAG
END AS 'DERIVED_FLAG'
Sample data set:
Client | Limit | Pdct_type_c | SRCE_SYST_FLAG | GENERIC_FLAG | TRAN_ID
--------------------------------------------------------------------------------
John | 4,000,000.00 | DCO | y | N | 2222
John | | DCO | NULL | N | 2222
Mark | 2,000,000.00 | DCO | NULL | N | 435
Mark | | DCO | NULL | N | 435
Luke | 4,000,000.00 | DCO | N | Y | 980
Desired result:
Client | Limit | Pdct_type_c | DERIVED_FLAG | TRAN_ID
---------------------------------------------------------------
John | 4,000,000.00 | DCO | Y | 2222
John | | DCO | Y | 2222
Mark | 2,000,000.00 | DCO | N | 435
Mark | | DCO | N | 435
Luke | 4,000,000.00 | DCO | Y | 980
I appreciate any assistance,
Cheers
Use SUM OVER to count the number of 'Y' per tran_id:
select client
, limit
, Pdct_type_c
, TRAN_ID
, case when SUM(case when SRCE_SYST_FLAG = 'Y' then 1 else 0 end) OVER (partition by tran_id) > 0 then 'Y' else GENERIC_FLAG end DERIVED_FLAG
from table

Return one row when multiples are returned

Firstly apologies for this as a number of similar posts have been posted but I just can't seem to return what I would like
My data returns
desc | date | taken | result | text | notes | page | group | q | answer | value | state | time |
------------------------------------------------------------------------------
Asess1 | 20170101 | John | 5 | Injury | xxx | Page1 | Assess11 | 1 | 1234567 | 1 | 1 | 0 |
Asess1 | 20170101 | John | 5 | Injury | xxx | Page1 | Assess11 | 1 | 1234567 | 1 | 1 | 0 |
Asess1 | 20170101 | John | 5 | Injury | xxx | Page1 | Assess11 | 1 | 1234567 | 1 | 1 | 0 |
Asess1 | 20170101 | John | 5 | Injury | xxx | Page1 | Assess11 | 1 | 1234567 | 1 | 1 | 0 |
Asess1 | 20170101 | John | 5 | Injury | xxx | Page1 | Assess11 | 1 | 1234567 | 1 | 1 | 0 |
Code as follows
select t.desc,a.date,a.taken,a.result,a.text,a.notes,d.page,d.group,d.q,d.answer,d.value,d.state,d.timeSpanSeconds
from cc_clientAssessments a
inner join cs_assessmentData d on a.assessmentId=d.assessment
inner join cs_clients c on c.person=a.residentId
inner join cs_facilities f on f.guid=a.facilityId
inner join cs_assessmentTypes t on t.assessmentTypeId=a.assessmentTypeId
where c.surname='smith'
and f.name='home'
and t.description ='injury'
and a.dateTaken='2017-05-28 00:00:00.000'
and d.questionName='1'
and d.answer='1234567'
order by t.desc, a.date desc,d.page,d.group,d.q
any help would be great.
One (or more) of your joins is causing this duplication, because you have not been specific enough in your join criteria.
As other commenters have said, remove all your joins and then add them back in one by one until you start to see duplicates. Using select * you can see what additional data is being pulled back and therefore what additional filters you need to include in that join. Once you have no duplication, add in the next join and repeat the whole process.
This is the sensible way to resolve this as nine times out of ten you can stop this duplication with more specific join criteria, which will also ensure your query is processing less data and will therefore be more efficient.
Although the elegant solution is to fix your joins so they don't result in as many rows, a very quick fix would be to just use distinct to eliminate duplicates and convert your text field to an string, so it can be compared.
select distinct t.desc,a.date,a.taken,a.result,substring(a.text,1,512) as text,a.notes,d.page,d.group,d.q,d.answer,d.value,d.state,d.timeSpanSeconds
from cc_clientAssessments a
inner join cs_assessmentData d on a.assessmentId=d.assessment
inner join cs_clients c on c.person=a.residentId
inner join cs_facilities f on f.guid=a.facilityId
inner join cs_assessmentTypes t on t.assessmentTypeId=a.assessmentTypeId
where c.surname='smith'
and f.name='home'
and t.description ='injury'
and a.dateTaken='2017-05-28 00:00:00.000'
and d.questionName='1'
and d.answer='1234567'
order by t.desc, a.date desc,d.page,d.group,d.q

SQL Server join query optimization with group by

Just for knowledge, I want to know that, can the below given query be achieve by any other feasible way like using group by.
SELECT
GROUPMAS.GRPCODE, GROUPMAS.GRPNAME,
GRPDTLS.ACCODE, GRPDTLS.ACNAME, GRPDTLS.DOA "ADMISSION DATE",
LOANMAST.LOANCODE, LOANMAST.VCHDATE "LOAN SANCTION DATE",
LOANMAST.LANAMT,
(SELECT SUM(RECPDTLS.INSTAMT)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS REPAYMENT,
(SELECT SUM(RECPDTLS.INTAMT)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS INTREST,
(SELECT MAX(RECPDTLS.VCHDATE)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS "LAST PAYMENT ON"
FROM
GROUPMAS
JOIN
GRPDTLS ON (GROUPMAS.GRPCODE = GRPDTLS.GRPCODE AND GRPDTLS.DOA <= '2009-03-31')
JOIN
LOANMAST ON (GRPDTLS.GRPCODE = LOANMAST.GRPCODE AND GRPDTLS.ACCODE = LOANMAST.ACCODE AND LOANMAST.VCHDATE <= '2009-03-31')
Table GROUPMAS structure
GRPCODE | GRPNAME
--------| -------
1 | A
2 | B
Table GRPDTLS structure
GRPCODE | ACCODE | ACNAME | DOA
--------|--------|--------|-----
1 | 1 | name1A | 2007-07-05
1 | 2 | name2A | 2008-07-05
2 | 1 | name1B | 2007-07-06
2 | 2 | name2B | 2007-07-05
Table LOANMAST structure
LOANCODE | GRPCODE | ACCODE | VCHDATE | LANAMT
---------|---------|--------|--------- |--------
1 | 1 | 2 |2009-01-06|2000
2 | 2 | 1 |2008-09-06|5000
Table RECPDTLS structure
TXNNO | LOANCODE | INSTAMT | INTAMT | VCHDATE
------|----------|---------|--------|---------
1 | 1 | 200 | 0 | 2009-02-06
2 | 1 | 200 | 10 | 2009-03-06
3 | 2 | 500 | 0 | 2008-10-06
4 | 2 | 1500 | 50 | 2009-03-28
5 | 2 | 500 | 0 | 2010-03-28
It will output something like this
GRPCODE | GRPNAME | ACCODE | ACNAME | ADMISSION DATE | LOANCODE | LOAN SANCTION DATE | LANAMT | REPAYMENT | INTREST | LAST PAYMENT ON
--------| --------| -------| ------ | ---------------| -------- | ------------------ | -------| ----------| ------- | --------------
1 | A | 2 | name2A | 2008-07-05 | 1 |2009-01-06 | 2000 | 400 | 10 | 2009-03-06
2 | B | 1 | name1B | 2007-07-06 | 2 |2008-09-06 | 5000 | 2000 | 50 | 2009-03-28
Thanks for the help.
You can replace the sub queries in your select statement with LEFT OUTER OR INNER JOIN depending on requirements. If all LOANCODE records will have matching RECPDTLS records then use INNER JOIN else use LEFT OUTER JOIN. Keep your aggregate functions the same.
...
Repayment=SUM(RECPDTLS.INSTAMT),
Interest=SUM(RECPDTLS.INTAMT),
LastPaymentOn=MAX(RECPDTLS.VCHDATE)
...
LEFT OUTER/INNER JOIN RECPDTLS ON RECPDTLS.LOANCODE = LOANMAST.LOANCODE AND Repayment.VCHDATE <= #HighDate
...
GROUP BY
GROUPMAS.GRPCODE, GROUPMAS.GRPNAME,
GRPDTLS.ACCODE, GRPDTLS.ACNAME, GRPDTLS.DOA,
LOANMAST.LOANCODE, LOANMAST.VCHDATE,
LOANMAST.LANAMT
You will need to run the query analyzer to see the efficiency gain between the old and the new queries.
NOTE : As I said above, be sure to use LET OUTER JOIN if the LOANCODE is not required to have a RECPDTLS as an INNER JOIN will only return matches in both tables.
You can use CTE to simplify the request :
;WITH LOANMASTAGG AS
(
SELECT SUM(r.INSTAMT) REPAYMENT, SUM(r.INTAMT) INTREST, MAX(r.VCHDATE) [LAST PAYMENT ON], l.LOANCODE, l.VCHDATE, l.LANAMT, l.ACCODE, l.GRPCODE
FROM #RECPDTLS r
INNER JOIN #LOANMAST l ON r.LOANCODE = l.LOANCODE
WHERE l.VCHDATE <= '2009-03-31'
GROUP BY l.LOANCODE, l.VCHDATE, l.LANAMT, l.ACCODE, l.GRPCODE
)
SELECT
g.GRPCODE,
g.GRPNAME,
gl.ACCODE,
gl.ACNAME,
gl.DOA "ADMISSION DATE",
la.LOANCODE,
la.VCHDATE "LOAN SANCTION DATE",
la.LANAMT,
la.REPAYMENT AS REPAYMENT,
la.INTREST AS INTREST,
la.[LAST PAYMENT ON] "LAST PAYMENT ON"
FROM LOANMASTAGG la
INNER JOIN #GRPDTLS gl ON gl.GRPCODE = la.GRPCODE AND gl.ACCODE = la.ACCODE
INNER JOIN #GROUPMAS g ON (g.GRPCODE = gl.GRPCODE)
WHERE gl.DOA <= '2009-03-31'

How to create a cross tab (in crystal) from multiple columns (in sql)

I have 5 columns in SQL that I need to turn into a cross tab in Crystal.
This is what I have:
Key | RELATIONSHIP | DISABLED | LIMITED | RURAL | IMMIGRANT
-----------------------------------------------------------------
1 | Other Dependent | Yes | No | No | No
2 | Victim/Survivor | No | No | No | No
3 | Victim/Survivor | Yes | No | No | No
4 | Child | No | No | No | No
5 | Victim/Survivor | No | No | No | No
6 | Victim/Survivor | No | No | No | No
7 | Child | No | No | No | No
8 | Victim/Survivor | No | Yes | Yes | Yes
9 | Child | No | Yes | Yes | Yes
10 | Child | No | Yes | Yes | Yes
This is what I want the cross tab to look like (Distinct count on Key):
| Victim/Survivor | Child | Other Dependent | Total |
--------------------------------------------------------------
| DISABLED | 1 | 0 | 1 | 2 |
--------------------------------------------------------------
| LIMITED | 1 | 2 | 0 | 3 |
--------------------------------------------------------------
| RURAL | 1 | 2 | 0 | 3 |
--------------------------------------------------------------
| IMMIGRANT | 1 | 2 | 0 | 3 |
--------------------------------------------------------------
| TOTAL | 4 | 6 | 1 | 11 |
--------------------------------------------------------------
I used this formula in Crystal in an effort to combine 4 columns (Field name = {#OTHERDEMO})...
IF {TABLE.DISABLED} = "YES" THEN "DISABLED" ELSE
IF {TABLE.LIMITED} = "YES" THEN "LIMITED" ELSE
IF {TABLE.IMMIGRANT} = "YES" THEN "IMMIGRANT" ELSE
IF {TABLE.RURAL} = "YES" THEN "RURAL"
...then made the cross-tab with #OTHERDEMO as the rows, RELATIONSHIP as the Columns with a distinct count on KEY:
Problem is, once crystal hits the first "Yes" it stops counting thus not categorizing correctly in the cross-tab. So I get a table that counts the DISABILITY first and gives the correct display, then counts the Limited and gives some info, but then dumps everything else.
In the past, I have done mutiple conditional formulas...
IF {TABLE.DISABLED} = "YES" AND {TABLE.RELATIONSHIP} = "Victim/Survivor" THEN {TABLE.KEY} ELSE {#NULL}
(the #null formula is because Crystal, notoriously, gets confused with nulls.)
...then did a distinct count on Key, and finally summed it in the footer.
I am convinced there is another way to do this. Any help/ideas would be greatly appreciated.
If you unpivot those "DEMO" columns into rows it will make the crosstab super easy...
select
u.[Key],
u.[RELATIONSHIP],
u.[DEMO]
from
Table1
unpivot (
[b] for [DEMO] in ([DISABLED], [LIMITED], [RURAL], [IMMIGRANT])
) u
where
u.[b] = 'Yes'
SqlFiddle
or if you were stuck on SQL2000 compatibility level you could manually unpivot the Yes values...
select [Key], [REALTIONSHIP], [DEMO] = cast('DISABLED' as varchar(20))
from Table1
where [DISABLED] = 'Yes'
union
select [Key], [REALTIONSHIP], [DEMO] = cast('LIMITED' as varchar(20))
from Table1
where [LIMITED] = 'Yes'
union
select [Key], [REALTIONSHIP], [DEMO] = cast('RURAL' as varchar(20))
from Table1
where [RURAL] = 'Yes'
union
select [Key], [REALTIONSHIP], [DEMO] = cast('IMMIGRANT' as varchar(20))
from Table1
where [IMMIGRANT] = 'Yes'
For the crosstab, use a count on the Key column (aka row count), [DEMO] on rows, and [RELATIONSHIP] on columns.

how to check date records with in fdate and tdate range in sql server

Hi friends i have small doubt in sql server
here i want data based on condition
same id and status is equal to s then that date value be
how to write query in sql server
Table :emp
id |status |date(mm-dd-yy) |fdate(mm-dd-yy) |tdate(mm-dd-yy)
1 | S |03-16-11 | |
1 | b | | 03-15-11 |03-18-11
1 | s |03-17-11 | |
1 | b | | 04-20-12 |04-30-12
1 | S |04-20-12 | |
1 | s |04-10-12 | |
1 | s |10-01-14 | |
1 | b | |10-02-14 |10-25-14
2 | s |01-18-12 | |
2 | b | |01-18-12 |01-28-12
2 | b | |03-10-13 |03-24-13
2 | s |03-16-13 | |
2 | s |03-10-13 | |
2 | s |03-23-13 | |
2 | b | |04-20-13 |04-27-13
2 | s |07-01-14 | |
the table (status = s, id, date) compare it with status = b, same id number and date ( Date value from status s) with the date range of fdate and tdate .
if that data with in range then Billing yes other wise billing no
output like
id |status |date(mm-dd-yy) |fdate(mm-dd-yy) |tdate(mm-dd-yy) |Billing
1 | S |03-16-11 | | |yes
1 | s |03-17-11 | | |yes
1 | S |04-20-12 | | |yes
1 | s |04-10-12 | | |no
1 | s |10-01-14 | | |no
2 | s |01-18-12 | | |yes
2 | s |03-16-13 | | |yes
2 | s |03-10-13 | | |yes
2 | s |03-23-13 | | |yes
2 | s |07-01-14 | | |no
i tried query like below
select *
from ( select * from emp a where status ='s') a
inner join (select * from emp b where status='b') b
on a.pn=b.pn
where a.date<=b.date1 and a.date>=b.date2
its not give exactely result.
please tell me how to write query in sql server .
Try
select a.Id,
a.status,
a.date,
a.fdate,
a.tdate,
max(IsNull(case when a.date between b.fDate and b.tDate
then 'yes'
else 'no'
end, 'no')) Billing
from emp a
left join emp b
on a.Id=b.Id
where a.status ='s'
and b.status = 'b'
group by a.Id,
a.status,
a.date,
a.fdate,
a.tdate
Some questions/comments:
What are the fields: pn, date1 and date2?
date1 in your query is, I guess, bigger than date2

Resources