Find the sum of amount for particular transasaction type for 2 consecutive days - sql

I have two tables 1. Transactions and 2. Transaction type
e.g. of transaction table with dummy data
account_key Transaction_key date amount
1 11 03/22/0219 5000
1 12 03/23/2019 6000
1 13 03/22/2019 4000
1 14 03/23/2019 3000
e.g. of Transaction_type table with dummy data
transaction_key transaction_type
11 in
12 in
13 out
14 out
I have to find the ratio of sum of amount for 2 consecutive days of similar transaction type for the same account key. for eg (5000+6000)/(4000+3000)
the database is oracle and datatype is datetime
This is what I have tried
select t1.account_key,
trunc(t1.date),
sum(t1.amount) as in,
sum(t2.amount) as out
from transactions t1
inner join transactions t2 on t1.accountkey=t2.accountkey
where t1.date between '01-jun-2017' and '30-nov-2017'
and t2.date between '01-jun-2017' and '30-nov-2017'
and t1.transaction_key in (select a.transaction_key
from transaction_type a
where a.transaction type in 'in')
and t2.transaction_key in (select b.transaction_key
from transaction_type b
where b.transaction type in 'out')
group by t1.account_key,
trunc(t1.date)
having max(trunc(t1.date))-min(trunc(t1.date)) = 1
and max(trunc(t2.date))-min(trunc(t2.date)) = 1

You can use a WITH clause, also called a "common table expression" or "CTE" to break your problem down into manageable chunks.
Here is one way to do that:
WITH txn_by_date AS (
SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t
INNER JOIN txn_types tt on tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type )
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM txn_by_date tin
INNER JOIN txn_by_date tout ON tout.txn_date = tin.txn_date + 1
AND tout.transaction_type = 'out'
AND tout.account_key = tin.account_key
WHERE tin.transaction_type = 'in';
The CTE first computes the total txn amount by account, transaction type, and day. Once you have that, the main SELECT gets each 'in' total, joins it with the 'out' total from the consecutive day, and the computes the ratio.
Here is a full example, with test data (also expressed using the WITH clause):
with txns (account_key, Transaction_key, txn_date, amount) AS
(
SELECT 1, 11, TO_DATE('03/22/2019','MM/DD/YYYY'), 5000 FROM DUAL UNION ALL
SELECT 1, 12, TO_DATE('03/23/2019','MM/DD/YYYY'), 6000 FROM DUAL UNION ALL
SELECT 1, 13, TO_DATE('03/22/2019','MM/DD/YYYY'), 4000 FROM DUAL UNION ALL
SELECT 1, 14, TO_DATE('03/23/2019','MM/DD/YYYY'), 3000 FROM DUAL ),
txn_types ( transaction_key, transaction_type ) AS
(
SELECT 11, 'in' FROM DUAL UNION ALL
SELECT 12, 'out' FROM DUAL UNION ALL
SELECT 13, 'in' FROM DUAL UNION ALL
SELECT 14, 'out' FROM DUAL ),
txn_by_date AS (
SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t
INNER JOIN txn_types tt on tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type )
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM txn_by_date tin
INNER JOIN txn_by_date tout ON tout.txn_date = tin.txn_date + 1
AND tout.transaction_type = 'out'
AND tout.account_key = tin.account_key
WHERE tin.transaction_type = 'in';
+-------------+-----------+-----------+------------+------------+-------+
| ACCOUNT_KEY | TXN_DATE | AMOUNT_IN | TXN_DATE_1 | AMOUNT_OUT | RATIO |
+-------------+-----------+-----------+------------+------------+-------+
| 1 | 22-MAR-19 | 9000 | 23-MAR-19 | 9000 | 1 |
+-------------+-----------+-----------+------------+------------+-------+
ALTERNATE VERSION, FOR ANCIENT VERSIONS OF ORACLE
For really old versions of Oracle, you may need to avoid both the WITH clause and ANSI-style joins. Here is the above query rewritten to avoid those features.
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM ( SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t,
txn_types tt
WHERE tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type ) tin,
( SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t,
txn_types tt
WHERE tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type ) tout
WHERE tin.transaction_type = 'in'
AND tout.transaction_type(+) = 'out'
AND tout.account_key(+) = tin.account_key;

I would try to use to Temporary Tables. Store the sums of each in Transaction type in two temp tables, then use them to calculate your ratio. I have not tested this query but this is basically what you are looking for:
CREATE PRIVATE TEMPORARY TABLE as In_TempTable
Select T2.Transaction_Type, sum(T1.amount) In_Total
from Transactions T1
left join Transaction_Type T2 on T2.Transaction_key = T1.Transaction_Key
where T1.date between '01-jun-2017' and '30-nov-2017'
AND T2.Transactio_Type = 'In'
group by T2.Transaction_Type;
CREATE PRIVATE TEMPORARY TABLE as Out_TempTable
Select T2.Transaction_Type, sum(T1.amount) Out_Total
from Transactions T1
left join Transaction_Type T2 on T2.Transaction_key = T1.Transaction_Key
where T1.date between '01-jun-2017' and '30-nov-2017'
AND T2.Transactio_Type = 'Out'
group by T2.Transaction_Type;
Select Sum(a.In_Total)/Sum(b.Out_Total)
from In_TempTable a
Full Outer Join Out_TempTable b on b.Transaction_Type = a.Transaction_Type
I would personally change this:
T1.date between '01-jun-2017' and '30-nov-2017'
To something like this
T1.date between startdate and startdate+2
Depending on your needs of course and you will have to declare your startdate

Related

oracle sql get transactions between the period

I have 3 tables in oracle sql namely investor, share and transaction.
I am trying to get new investors invested in any shares for a certain period. As they are the new investor, there should not be a transaction in the transaction table for that investor against that share prior to the search period.
For the transaction table with the following records:
Id TranDt InvCode ShareCode
1 2020-01-01 00:00:00.000 inv1 S1
2 2019-04-01 00:00:00.000 inv1 S1
3 2020-04-01 00:00:00.000 inv1 S1
4 2021-03-06 11:50:20.560 inv2 S2
5 2020-04-01 00:00:00.000 inv3 S1
For the search period between 2020-01-01 and 2020-05-01, I should get the output as
5 2020-04-01 00:00:00.000 inv3 S1
Though there are transactions for inv1 in the table for that period, there is also a transaction prior to the search period, so that shouldn't be included as it's not considered as new investor within the search period.
Below query is working but it's really taking ages to return the results calling from c# code leading to timeout issues. Is there anything we can do to refine to get the results quicker?
WITH
INVESTORS AS
(
SELECT I.INVCODE FROM INVESTOR I WHERE I.CLOSED IS NULL)
),
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
),
SHARES_IN_PERIOD AS
(
SELECT DISTINCT
T.INVCODE,
T.SHARECODE,
T.TYPE
FROM TRANSACTION T
JOIN INVESTORS I ON T.INVCODE = I.INVCODE
JOIN SHARES S ON T.SHARECODE = S.SHARECODE
WHERE T.TRANDT >= :startDate AND T.TRANDT <= :endDate
),
PREVIOUS_SHARES AS
(
SELECT DISTINCT
T.INVCODE,
T.SHARECODE,
T.TYPE
FROM TRANSACTION T
JOIN INVESTORS I ON T.INVCODE = I.INVCODE
JOIN SHARES S ON T.TRSTCODE = S.TRSTCODE
WHERE T.TRANDT < :startDate
)
SELECT
DISTINCT
SP.INVCODE AS InvestorCode,
SP.SHARECODE AS ShareCode,
SP.TYPE AS ShareType
FROM SHARES_IN_PERIOD SP
WHERE (SP.INVCODE, SP.SHARECODE, SP.TYPE) NOT IN
(
SELECT
PS.INVCODE,
PS.SHARECODE,
PS.TYPE
FROM PREVIOUS_SHARES PS
)
With the suggestion given by #Gordon Linoff, I tried following options (for all the shares I need) but they are taking long time too. Transaction table is over 32 million rows.
1.
WITH
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
)
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
join shares s on s.sharecode = t.sharecode
where seqnum = 1 and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
WITH
INVESTORS AS
(
SELECT I.INVCODE FROM INVESTOR I WHERE I.CLOSED IS NULL)
),
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
)
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
join investors i on i.invcode = t.invcode
join shares s on s.sharecode = t.sharecode
where seqnum = 1 and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
where seqnum = 1 and
t.sharecode IN (SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL)))
and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
If you want to know if the first record in transactions for a share is during a period, you can use window functions:
select t.*
from (select t.*,
row_number() over (partition by invcode, sharecode order by trandt) as seqnum
from transactions t
) t
where seqnum = 1 and
t.sharecode = :sharecode and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
For performance for this code, you want an index on transactions(invcode, sharecode, trandate).

Oracle SQL Hierarchy Summation

I have a table TRANS that contains the following records:
TRANS_ID TRANS_DT QTY
1 01-Aug-2020 5
1 01-Aug-2020 1
1 03-Aug-2020 2
2 02-Aug-2020 1
The expected output:
TRANS_ID TRANS_DT BEGBAL TOTAL END_BAL
1 01-Aug-2020 0 6 6
1 02-Aug-2020 6 0 6
1 03-Aug-2020 6 2 8
2 01-Aug-2020 0 0 0
2 02-Aug-2020 0 1 1
2 03-Aug-2020 1 0 1
Each trans_id starts with a beginning balance of 0 (01-Aug-2020). For succeeding days, the beginning balance is the ending balance of the previous day and so on.
I can create PL/SQL block to create the output. Is it possible to get the output in 1 SQL statement?
Thanks.
Try this following script using CTE-
Demo Here
WITH CTE
AS
(
SELECT DISTINCT A.TRANS_ID,B.TRANS_DT
FROM your_table A
CROSS JOIN (SELECT DISTINCT TRANS_DT FROM your_table) B
),
CTE2
AS
(
SELECT C.TRANS_ID,C.TRANS_DT,SUM(D.QTY) QTY
FROM CTE C
LEFT JOIN your_table D
ON C.TRANS_ID = D.TRANS_ID
AND C.TRANS_DT = D.TRANS_DT
GROUP BY C.TRANS_ID,C.TRANS_DT
ORDER BY C.TRANS_ID,C.TRANS_DT
)
SELECT F.TRANS_ID,F.TRANS_DT,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT < F.TRANS_DT
) BEGBAL,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT = F.TRANS_DT
) TOTAL ,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT <= F.TRANS_DT
) END_BAL
FROM CTE2 F
You can as well do like this (I would assume it's a bit faster): Demo
with
dt_between as (
select mindt + level - 1 as trans_dt
from (select min(trans_dt) as mindt, max(trans_dt) as maxdt from t)
connect by level <= maxdt - mindt + 1
),
dt_for_trans_id as (
select *
from dt_between, (select distinct trans_id from t)
),
qty_change as (
select distinct trans_id, trans_dt,
sum(qty) over (partition by trans_id, trans_dt) as total,
sum(qty) over (partition by trans_id order by trans_dt) as end_bal
from t
right outer join dt_for_trans_id using (trans_id, trans_dt)
)
select
trans_id,
to_char(trans_dt, 'DD-Mon-YYYY') as trans_dt,
nvl(lag(end_bal) over (partition by trans_id order by trans_dt), 0) as beg_bal,
nvl(total, 0) as total,
nvl(end_bal, 0) as end_bal
from qty_change q
order by trans_id, trans_dt
dt_between returns all the days between min(trans_dt) and max(trans_dt) in your data.
dt_for_trans_id returns all these days for each trans_id in your data.
qty_change finds difference for each day (which is TOTAL in your example) and cumulative sum over all the days (which is END_BAL in your example).
The main select takes END_BAL from previous day and calls it BEG_BAL, it also does some formatting of final output.
First of all, you need to generate dates, then you need to aggregate your values by TRANS_DT, and then left join your aggregated data to dates. The easiest way to get required sums is to use analitic window functions:
with dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
)
,trans_agg as ( -- aggregating QTY in TRANS
select TRANS_ID,TRANS_DT,sum(QTY) as QTY
from trans
group by TRANS_ID,TRANS_DT
)
select -- using left join partition by to get data on daily basis for each trans_id:
dt,
trans_id,
nvl(sum(qty) over(partition by trans_id order by dates.dt range between unbounded preceding and 1 preceding),0) as BEGBAL,
nvl(qty,0) as TOTAL,
nvl(sum(qty) over(partition by trans_id order by dates.dt),0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
Full example with sample data:
alter session set nls_date_format='dd-mon-yyyy';
with trans(TRANS_ID,TRANS_DT,QTY) as (
select 1,to_date('01-Aug-2020'), 5 from dual union all
select 1,to_date('01-Aug-2020'), 1 from dual union all
select 1,to_date('03-Aug-2020'), 2 from dual union all
select 2,to_date('02-Aug-2020'), 1 from dual
)
,dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
)
,trans_agg as ( -- aggregating QTY in TRANS
select TRANS_ID,TRANS_DT,sum(QTY) as QTY
from trans
group by TRANS_ID,TRANS_DT
)
select
dt,
trans_id,
nvl(sum(qty) over(partition by trans_id order by dates.dt range between unbounded preceding and 1 preceding),0) as BEGBAL,
nvl(qty,0) as TOTAL,
nvl(sum(qty) over(partition by trans_id order by dates.dt),0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
You can use a recursive query to generate the overall date range, cross join it with the list of distinct tran_id, then bring the table with a left join. The last step is aggregation and window functions:
with all_dates (trans_dt, max_dt) as (
select min(trans_dt), max(trans_dt) from trans group by trans_id
union all
select trans_dt + interval '1' day, max_dt from all_dates where trans_dt < max_dt
)
select
i.trans_id,
d.trans_dt,
coalesce(sum(sum(t.qty)) over(partition by i.trans_id order by d.trans_dt), 0) - coalesce(sum(t.qty), 0) begbal,
coalesce(sum(t.qty), 0) total,
coalesce(sum(sum(t.qty)) over(partition by i.trans_id order by d.trans_dt), 0) endbal
from all_dates d
cross join (select distinct trans_id from trans) i
left join trans t on t.trans_id = i.trans_id and t.trans_dt = d.trans_dt
group by i.trans_id, d.trans_dt
order by i.trans_id, d.trans_dt

Full Outer Join, Coalesce, and Group By (Oh My!)

I'm going to ask this in two parts, because my logic may be way off, and if so, the syntax doesn't really matter.
I have 10 queries. Each query returns month, supplier, and count(some metric). The queries use various tables, joins, etc. Not all month/supplier combinations exist in the output for each query. I would like to combine these into a single data set that can be exported and pivoted on in Excel.
I'd like the output to look like this:
Month | Supplier | Metric1 |Metric2 |..| Metric 10
2018-01 | Supp1 | _value_ | _value_ |...| _value_ |
2018-01 | Supp2 | NULL | _value_ |...| NULL
What is the best / easiest / most efficient way to accomplish this?
I've tried various methods to accomplish the above, but I can't seem to get the syntax quite right. I wanted to make a very simple test case and build upon it, but I only have select privileges on the db, so I am unable to test it out. I was able to create a query that at least doesn't result in any squiggly red error lines, but applying the same logic to the bigger problem doesn't work.
This is what I've got:
create table test1(name varchar(20),credit int);
insert into test1 (name, credit) values ('Ed',1),('Ann',1),('Jim',1),('Ed',1),('Ann',1);
create table test2 (name varchar(10), debit int);
insert into test2 (name, debit) values ('Ann',1),('Sue',1),('Sue',1),('Sue',1);
select
coalesce(a.name, b.name) as name,
cred,
deb
from
(select name, count(credit) as cred
from test1
group by name) a
full outer join
(select name, count(debit) as deb
from test2
group by name) b on
a.name =b.name;
Am I headed down the right path?
UPDATE: Based on Gordon's input, I tried this on the first two queries:
select Month, Supp,
sum(case when which = 1 then metric end) as Exceptions,
sum(case when which = 2 then metric end) as BackOrders
from (
(
select Month, Supp, metric, 1 as which
from (
select (convert(char(4),E.PostDateTime,120)+'-'+convert(char(2),E.PostDateTime,101)) as Month, E.TradingPartner as Supp, count(distinct(E.excNum)) as metric
from db..TrexcMangr E
where (E.DSHERep in ('AVR','BTB') OR E.ReleasedBy in ('AVR','BTB')) AND year(E.PostDateTime) >= '2018'
) a
)
union all
(
select Month, Supp, metric, 2 as which
from (
select (convert(char(4),T.UpdatedDateTime,120)+'-'+convert(char(2),T.UpdatedDateTime,101)) as Month, P.Supplier as Supp, count(*) as metric
from db1..trordertext T
inner join mdid_Tran..trOrderPO P on P.PONum = T.RefNum
where T.TextType = 'BO' AND (T.CreatedBy in ('AVR','BTB') OR T.UpdatedBy in ('AVR','BTB')) AND year(UpdatedDateTime) >=2018
) b
)
) q
group by Month, Supp
... but I'm getting a group by error.
One method uses union all and group by:
select month, supplier,
sum(case when which = 1 then metric end) as metric_01,
sum(case when which = 2 then metric end) as metric_02,
. . .
from ((select Month, Supplier, Metric, 1 as which
from (<query1>) q
. . .
) union all
(select Month, Supplier, Metric, 2 as which
from (<query2>) q
. . .
) union all
. . .
) q
group by month, supplier;
SELECT
CalendarMonthStart,
Supp,
SUM(CASE WHEN metric_id = 1 THEN metric END) as Exceptions,
SUM(CASE WHEN metric_id = 2 THEN metric END) as BackOrders
FROM
(
SELECT
DATEADD(month, DATEDIFF(month, 0, E.PostDateTime), 0) AS CalendarMonthStart,
E.TradingPartner AS Supp,
COUNT(DISTINCT(E.excNum)) AS metric,
1 AS metric_id
FROM
db..TrexcMangr E
WHERE
( E.DSHERep in ('AVR','BTB')
OR E.ReleasedBy in ('AVR','BTB')
)
AND E.PostDateTime >= '2018-01-01'
GROUP BY
1, 2
UNION ALL
SELECT
DATEADD(month, DATEDIFF(month, 0, T.UpdatedDateTime), 0) AS CalendarMonthStart,
T.UpdatedDateTime,
P.Supplier AS Supp,
COUNT(*) AS metric,
2 AS metric_id
FROM
db1..trordertext T
INNER JOIN
mdid_Tran..trOrderPO P
ON P.PONum = T.RefNum
WHERE
( T.CreatedBy in ('AVR','BTB')
OR T.UpdatedBy in ('AVR','BTB')
)
AND T.TextType = 'BO'
AND T.UpdatedDateTime >= '2018-01-01'
GROUP BY
1, 2
)
combined
GROUP BY
CalendarMonthStart,
Supp

Getting rid of grouping field

Is there a safe way to not have to group by a field when using an aggregate in another field? Here is my example
SELECT
C.CustomerName
,D.INDUSTRY_CODE
,CASE WHEN D.INDUSTRY_CODE IN ('003','004','005','006','007','008','009','010','017','029')
THEN 'PM'
WHEN UPPER(CustomerName) = 'ULINE INC'
THEN 'ULINE'
ELSE 'DR'
END AS BU
,ISNULL((SELECT SUM(GrossAmount)
where CONVERT(date,convert(char(8),InvoiceDateID )) between DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) - 1, 0) and DATEADD(year, -1, GETDATE())),0) [PREVIOUS YEAR GROSS]
FROM factMargins A
LEFT OUTER JOIN dimDate B ON A.InvoiceDateID = B.DateId
LEFT OUTER JOIN dimCustomer C ON A.CustomerID = C.CustomerId
LEFT OUTER JOIN CRCDATA.DBO.CU10 D ON D.CUST_NUMB = C.CustomerNumber
GROUP BY
C.CustomerName,D.INDUSTRY_CODE
,A.InvoiceDateID
order by CustomerName
before grouping I was only getting 984 rows but after grouping by the A.InvoiceDateId field I am getting over 11k rows. The rows blow up since there are multiple invoices per customer. Min and Max wont work since then it will pull data incorrectly. Would it be best to let my application (crystal) get rid of the extra lines? Usually I like to have my base data be as close as possible to how the report will layout if possible.
Try moving the reference to InvoiceDateID to within an aggregate function, rather than within a selected subquery's WHERE clause.
In Oracle, here's an example:
with TheData as (
select 'A' customerID, 25 AMOUNT , trunc(sysdate) THEDATE from dual union
select 'B' customerID, 35 AMOUNT , trunc(sysdate-1) THEDATE from dual union
select 'A' customerID, 45 AMOUNT , trunc(sysdate-2) THEDATE from dual union
select 'A' customerID, 11000 AMOUNT , trunc(sysdate-3) THEDATE from dual union
select 'B' customerID, 12000 AMOUNT , trunc(sysdate-4) THEDATE from dual union
select 'A' customerID, 15000 AMOUNT , trunc(sysdate-5) THEDATE from dual)
select
CustomerID,
sum(amount) as "AllRevenue"
sum(case when thedate<sysdate-3 then amount else 0 end) as "OlderRevenue",
from thedata
group by customerID;
Output:
CustomerID | AllRevenue | OlderRevenue
A | 26070 | 26000
B | 12035 | 12000
This says:
For each customerID
I want the sum of all amounts
and I want the sum of amounts earlier than 3 days ago

Results of multiple queries with aggregates combined

I have 2 seperate select statements, using aggregate functions in each. I would like to be able to take the results and combine them.
table_a
id int
entered_date datetime (holds utc stamp)
balance money
group_id int
table_b
id int
entered_date date
balance money
transaction_type int
query 1:
select convert(date,entered_date), sum(balance) as earned
from table_a
where group_id in (1, 2, 3, 4)
group by convert(date,entered_Date)
query 2:
select convert(date,entered_date), sum(balance) as spent
where transaction_type = 2
group by convert(date,entered_Date)
results:
query 1:
2012-05-13, 5000
2012-05-14, 12000
...
query 2:
2012-05-13, 9000
2012-05-14, 55856
...
I would like to return one row for each record without using temp tables. The result set should have a date, then earned vs. spent. I have a report running using union to get the totals and that is fine, but i need to generate a result set with 1 record and a earned vs against line. Any help with this is appreciated.
Try:
;With AllDates AS
(
select convert(date,entered_date) As EnteredDate
from table_a
where group_id in (1, 2, 3, 4)
group by convert(date,entered_Date)
UNION
select convert(date,entered_date)
from table_b
where transaction_type = 2
group by convert(date,entered_Date)
)
, AllEarned AS (
select convert(date,entered_date) AS EnteredDate, sum(balance) as Earned
from table_a
where group_id in (1, 2, 3, 4)
group by convert(date,entered_Date)
)
, AllSpent AS (
select convert(date,entered_date) AS EnteredDate, sum(balance) as Spent
from table_b
where transaction_type = 2
group by convert(date,entered_Date)
)
SELECT
d.EnteredDate, e.Earned, s.Spent
FROM AllDates d
LEFT OUTER JOIN AllEarned e ON d.EnteredDate=e.EnteredDate
LEFT OUTER JOIN AllSpent s ON d.EnteredDate=s.EnteredDate
ORDER BY 1,2,3
You can combine these using logic, assuming that both are from the same table
(the second query is missing the from statement):
select convert(date,entered_date),
sum(case when group_id in (1, 2, 3, 4) then balance end) as earned,
sum(case when transaction_type = 2 then balance end) as spend
from table_a
group by convert(date,entered_Date)
SELECT
CASE WHEN a.a_date IS NULL THEN b.a_date ELSE a.a_date END as a_data,
a.earned,
b.spent
FROM
(select
convert(date,entered_date) as a_date,
sum(balance) as earned
from table_a
where group_id in (1, 2, 3, 4)
group by entered_Date) A
FULL OUTER JOIN
(select
convert(date,entered_date) as a_date,
sum(balance) as spent
from table_a
where transaction_type = 2
group by entered_Date) B
ON A.a_date=b.a_date
Or using FULL OUTER JOIN if there are data that don't meet both conditions. And using CASE WHEN a.a_date IS NULL THEN b.a_date ELSE a.a_date END as a_data
Assuming earned amounts are from table_a and spent amounts are from table_b,
; WITH a AS (
select entered_date=convert(date,entered_date), balance as earned, 0 AS spent
from table_a
where group_id in (1, 2, 3, 4)
UNION ALL
select entered_date=convert(date,entered_date), 0 AS earned, balance as spent
from table_b
where transaction_type = 2
)
SELECT entered_date
, earned=SUM(earned)
, spent=SUM(spent)
FROM a
GROUP BY entered_date