Combining two queries Oracle SQL - sql

I'm trying to combine my two queries that work perfectly fine individually. But I'm stuck on trying to get them together to work as one and bring out the desired results.
The two queries are:
select clientid, sum(fee) as "Total Spent"
from bookings
group by clientid;
select l.clientid, sum(m.price * l.quantity) as "Total Spent"
from lineitems l
join merchandise m on m.merchid = l.merchid
group by l.clientid;
So the end goal is to combine the amount of money each client has spent for both bookings and purchasing.
i.e. Client ID 12 has spent $450 on bookings and $85 on products; so that would total to $535.
The set of data is this:
Bookings Table:
+----------+-------+------------+----------+-----------+---------+------------+
| ClientId | Tour | EventMonth | EventDay | EventYear | Payment | DateBooked |
+----------+-------+------------+----------+-----------+---------+------------+
| 12 | South | Feb | 20 | 2016 | 225 | 19/02/2016 |
| 12 | West | Mar | 5 | 2016 | 225 | 3/03/2016 |
+----------+-------+------------+----------+-----------+---------+------------+
LineItems Table:
+----------+-------+------------+----------+-----------+---------+-----+
| ClientID | Tour | EventMonth | EventDay | EventYear | MerchId | Qty |
+----------+-------+------------+----------+-----------+---------+-----+
| 12 | South | Feb | 20 | 2016 | 20 | 1 |
+----------+-------+------------+----------+-----------+---------+-----+
Merchandise Table:
+---------+----------+------------+-------+
| MerchID | Category | ProdName | Price |
+---------+----------+------------+-------+
| 20 | A | Highway | 85 |
+---------+----------+------------+-------+
Any help would be muchly appreciated

You can use join:
SELECT b.*, m.*, b.totalspent + c.totalspent
FROM (SELECT CLIENTID, SUM(FEE) AS TotalSpent
FROM BOOKINGS2017
GROUP BY CLIENTID
) b JOIN
(SELECT L.CLIENTID, SUM(M.PRICE * L.QUANTITY) AS TotalSpent
FROM LINEITEM2017 L JOIN
MERCHANDISE2017 M
ON L.MERCHID = M.MERCHID
GROUP BY L.CLIENTID
) m
USING (CLIENTID);
You may need an outer join if the tables have different sets of clients.

This is essentially the same as Gordon's answer but with your sample data inline and a grand total:
-- Your sample data:
with bookings (clientid, tour, eventmonth, eventday, eventyear, payment, datebooked ) as
( select 12, 'South', 'Feb', 20, 2016, 225, date '2016-02-19' from dual union all
select 12, 'West', 'Mar', 5, 2016, 225, date '2016-03-03' from dual union all
select 2, 'West', 'Mar', 5, 2016, 225, date '2016-03-03' from dual union all
select 2, 'West', 'Mar', 6, 2017, 225, date '2016-03-03' from dual union all
select 2, 'West', 'Mar', 7, 2018, 225, date '2016-03-03' from dual )
, lineitems (clientid, tour, eventmonth, eventday, eventyear, merchid, quantity) as
( select 12, 'South', 'Feb', 20, 2016, 20, 1 from dual )
, merchandise (merchid, category, prodname, price) as
( select 20, 'A', 'Highway', 85 from dual )
--
-- Actual query starts here
--
select b.clientid
, bookings_total
, coalesce(merchandise_total,0) as merchandise_total
, bookings_total + coalesce(merchandise_total,0) as grand_total
from ( select clientid, sum(payment) as bookings_total
from bookings
group by clientid ) b
left join
( select l.clientid, sum(l.quantity * m.price) as merchandise_total
from lineitems l
join merchandise m on m.merchid = l.merchid
group by clientid ) lm
on lm.clientid = b.clientid;
CLIENTID BOOKINGS_TOTAL MERCHANDISE_TOTAL GRAND_TOTAL
-------- -------------- ----------------- -----------
12 450 85 535
2 675 0 675

Related

Variable value as column name in Snowflake

can I obtain in a query variable value as column name in Snowflake?
SET "CURRENT_YEAR"=YEAR(CURRENT_DATE());
SELECT SUM("AMOUNT") AS "$CURRENT_YEAR" (here I want the value 2021)
FROM "DB"."SCHEMA"."TABLE"
WHERE YEAR("DATE") = $CURRENT_YEAR;
Please try below:
create or replace table test (
date date,
amount int
);
insert into test values
('2021-01-01', 100),
('2022-01-01', 56),
('2022-02-01', 67),
('2021-05-01', 38),
('2023-01-01', 150),
('2021-01-06', 400),
('2021-07-11', 120)
;
SET "CURRENT_YEAR"=YEAR(CURRENT_DATE());
with year_tbl as (
select year(date) as year, amount from test
where year = $CURRENT_YEAR
)
select *
from year_tbl
pivot(sum(amount) for year in ($CURRENT_YEAR)) as yr
;
+------+
| 2021 |
|------|
| 658 |
+------+
If you want different years:
with year_tbl as (
select year(date) as year, amount from test
)
select *
from year_tbl
pivot(sum(amount) for year in (2020, 2021, 2022, 2023)) as yr
;
+------+------+------+------+
| 2020 | 2021 | 2022 | 2023 |
|------+------+------+------|
| NULL | 658 | 123 | 150 |
+------+------+------+------+
did you mean something like this
create or replace table fld_year as
(SELECT current_date() dt, 2021 as fld_year, 1 as AMT UNION ALL
SELECT current_date(),2021 as fld_year, 2 as r_num UNION ALL
SELECT current_date()- 900,2019 as fld_year, 3 as r_num UNION ALL
SELECT current_date()-400,2020 as fld_year, 4 as r_num );
SET "CURRENT_YEAR"=YEAR(CURRENT_DATE());
SELECT SUM(AMT) FROM fld_year WHERE YEAR(dt) = $CURRENT_YEAR;
SELECT * FROM fld_year WHERE YEAR(dt) = $CURRENT_YEAR;

SQL FIFO query with group by

I have 3 tables:
INVENTORY_IN:
ID INV_TIMESTAMP PRODUCT_ID IN_QUANTITY SUPPLIER_ID
...
1 10.03.21 01:00:00 101 100 4
2 11.03.21 02:00:00 101 50 3
3 14.03.21 01:00:00 101 10 2
INVENTORY_OUT:
ID INV_TIMESTAMP PRODUCT_ID OUT_QUANTITY CUSTOMER_ID
...
1 10.03.21 02:00:00 101 30 1
2 11.03.21 01:00:00 101 40 2
3 12.03.21 01:00:00 101 80 1
INVENTORY_BALANCE:
INV_DATE PRODUCT_ID QUANTITY
...
09.03.21 101 20
10.03.21 101 90
11.03.21 101 100
12.03.21 101 20
13.03.21 101 20
14.03.21 101 30
I want to use FIFO (first in-first out) logic for the inventory, and to see which quantities correspond to each SUPPLIER-CUSTOMER combination.
The desired ouput looks like this (queried for dates >= 2021-03-10):
PRODUCT_ID SUPPLIER_ID CUSTOMER_ID QUANTITY
101 1 20
101 4 1 60
101 4 2 40
101 3 1 30
101 3 20
101 2 10
edit. fixed little typo in numbers.
edit. Added a diagram which explains every row. All of the black arrows correspond to supplier and customer combinations, there are 7 of them, because for supplier_id = 4 and customer_id = 1 the desired results is the sum of matched quantities happening between them. So, it explains why there are 7 arrows, while the desired results contains only 6 rows.
Option 1
This is probably a job for PL/SQL. Starting with the data types to output:
CREATE TYPE supply_details_obj AS OBJECT(
product_id NUMBER,
quantity NUMBER,
supplier_id NUMBER,
customer_id NUMBER
);
CREATE TYPE supply_details_tab AS TABLE OF supply_details_obj;
Then we can define a pipelined function to read the INVENTORY_IN and INVENTORY_OUT tables one row at a time and merge the two keeping a running total of the remaining inventory or amount to supply:
CREATE FUNCTION assign_suppliers_to_customers (
i_product_id IN INVENTORY_IN.PRODUCT_ID%TYPE
)
RETURN supply_details_tab PIPELINED
IS
v_supplier_id INVENTORY_IN.SUPPLIER_ID%TYPE;
v_customer_id INVENTORY_OUT.CUSTOMER_ID%TYPE;
v_quantity_in INVENTORY_IN.IN_QUANTITY%TYPE := NULL;
v_quantity_out INVENTORY_OUT.OUT_QUANTITY%TYPE := NULL;
v_cur_in SYS_REFCURSOR;
v_cur_out SYS_REFCURSOR;
BEGIN
OPEN v_cur_in FOR
SELECT in_quantity, supplier_id
FROM INVENTORY_IN
WHERE product_id = i_product_id
ORDER BY inv_timestamp;
OPEN v_cur_out FOR
SELECT out_quantity, customer_id
FROM INVENTORY_OUT
WHERE product_id = i_product_id
ORDER BY inv_timestamp;
LOOP
IF v_quantity_in IS NULL THEN
FETCH v_cur_in INTO v_quantity_in, v_supplier_id;
IF v_cur_in%NOTFOUND THEN
v_supplier_id := NULL;
END IF;
END IF;
IF v_quantity_out IS NULL THEN
FETCH v_cur_out INTO v_quantity_out, v_customer_id;
IF v_cur_out%NOTFOUND THEN
v_customer_id := NULL;
END IF;
END IF;
EXIT WHEN v_cur_in%NOTFOUND AND v_cur_out%NOTFOUND;
IF v_quantity_in > v_quantity_out THEN
PIPE ROW(
supply_details_obj(
i_product_id,
v_quantity_out,
v_supplier_id,
v_customer_id
)
);
v_quantity_in := v_quantity_in - v_quantity_out;
v_quantity_out := NULL;
ELSE
PIPE ROW(
supply_details_obj(
i_product_id,
v_quantity_in,
v_supplier_id,
v_customer_id
)
);
v_quantity_out := v_quantity_out - v_quantity_in;
v_quantity_in := NULL;
END IF;
END LOOP;
END;
/
Then, for the sample data:
CREATE TABLE INVENTORY_IN ( ID, INV_TIMESTAMP, PRODUCT_ID, IN_QUANTITY, SUPPLIER_ID ) AS
SELECT 0, TIMESTAMP '2021-03-09 00:00:00', 101, 20, 0 FROM DUAL UNION ALL
SELECT 1, TIMESTAMP '2021-03-10 01:00:00', 101, 100, 4 FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '2021-03-11 02:00:00', 101, 50, 3 FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '2021-03-14 01:00:00', 101, 10, 2 FROM DUAL;
CREATE TABLE INVENTORY_OUT ( ID, INV_TIMESTAMP, PRODUCT_ID, OUT_QUANTITY, CUSTOMER_ID ) AS
SELECT 1, TIMESTAMP '2021-03-10 02:00:00', 101, 30, 1 FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '2021-03-11 01:00:00', 101, 40, 2 FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '2021-03-12 01:00:00', 101, 80, 1 FROM DUAL;
The query:
SELECT product_id,
supplier_id,
customer_id,
SUM( quantity ) AS quantity
FROM TABLE( assign_suppliers_to_customers( 101 ) )
GROUP BY
product_id,
supplier_id,
customer_id
ORDER BY
MIN( inv_timestamp )
Outputs:
PRODUCT_ID | SUPPLIER_ID | CUSTOMER_ID | QUANTITY
---------: | ----------: | ----------: | -------:
101 | 0 | 1 | 20
101 | 4 | 1 | 60
101 | 4 | 2 | 40
101 | 3 | 1 | 30
101 | 3 | null | 20
101 | 2 | null | 10
Option 2
A (very) complicated SQL query:
WITH in_totals ( ID, INV_TIMESTAMP, PRODUCT_ID, IN_QUANTITY, SUPPLIER_ID, TOTAL_QUANTITY ) AS (
SELECT i.*,
SUM( in_quantity ) OVER ( PARTITION BY product_id ORDER BY inv_timestamp )
FROM inventory_in i
),
out_totals ( ID, INV_TIMESTAMP, PRODUCT_ID, OUT_QUANTITY, CUSTOMER_ID, TOTAL_QUANTITY ) AS (
SELECT o.*,
SUM( out_quantity ) OVER ( PARTITION BY product_id ORDER BY inv_timestamp )
FROM inventory_out o
),
split_totals ( product_id, inv_timestamp, supplier_id, customer_id, quantity ) AS (
SELECT i.product_id,
MIN( COALESCE( LEAST( i.inv_timestamp, o.inv_timestamp ), i.inv_timestamp ) )
AS inv_timestamp,
i.supplier_id,
o.customer_id,
SUM(
COALESCE(
LEAST(
i.total_quantity - o.total_quantity + o.out_quantity,
o.total_quantity - i.total_quantity + i.in_quantity,
i.in_quantity,
o.out_quantity
),
0
)
)
FROM in_totals i
LEFT OUTER JOIN
out_totals o
ON ( i.product_id = o.product_id
AND i.total_quantity - i.in_quantity <= o.total_quantity
AND i.total_quantity >= o.total_quantity - o.out_quantity )
GROUP BY
i.product_id,
i.supplier_id,
o.customer_id
ORDER BY
inv_timestamp
),
missing_totals ( product_id, inv_timestamp, supplier_id, customer_id, quantity ) AS (
SELECT i.product_id,
i.inv_timestamp,
i.supplier_id,
NULL,
i.in_quantity - COALESCE( s.quantity, 0 )
FROM inventory_in i
INNER JOIN (
SELECT product_id,
supplier_id,
SUM( quantity ) AS quantity
FROM split_totals
GROUP BY product_id, supplier_id
) s
ON ( i.product_id = s.product_id
AND i.supplier_id = s.supplier_id )
ORDER BY i.inv_timestamp
)
SELECT product_id, supplier_id, customer_id, quantity
FROM (
SELECT product_id, inv_timestamp, supplier_id, customer_id, quantity
FROM split_totals
WHERE quantity > 0
UNION ALL
SELECT product_id, inv_timestamp, supplier_id, customer_id, quantity
FROM missing_totals
WHERE quantity > 0
ORDER BY inv_timestamp
);
Which, for the sample data above, outputs:
PRODUCT_ID | SUPPLIER_ID | CUSTOMER_ID | QUANTITY
---------: | ----------: | ----------: | -------:
101 | 0 | 1 | 20
101 | 4 | 1 | 60
101 | 4 | 2 | 40
101 | 3 | 1 | 30
101 | 3 | null | 20
101 | 2 | null | 10
db<>fiddle here
If your system controls the timestamps so you cannot consume what was not supplied (I've met systems, that didn't track intraday balance), then you can use SQL solution with interval join. The only thing to take care here is to track the last supply that was not consumed in full: it should be added as supply with no customer.
Here's the query with comments:
CREATE TABLE INVENTORY_IN ( ID, INV_TIMESTAMP, PRODUCT_ID, IN_QUANTITY, SUPPLIER_ID ) AS
SELECT 0, TIMESTAMP '2021-03-09 00:00:00', 101, 20, 0 FROM DUAL UNION ALL
SELECT 1, TIMESTAMP '2021-03-10 01:00:00', 101, 100, 4 FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '2021-03-11 02:00:00', 101, 50, 3 FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '2021-03-14 01:00:00', 101, 10, 2 FROM DUAL;
CREATE TABLE INVENTORY_OUT ( ID, INV_TIMESTAMP, PRODUCT_ID, OUT_QUANTITY, CUSTOMER_ID ) AS
SELECT 1, TIMESTAMP '2021-03-10 02:00:00', 101, 30, 1 FROM DUAL UNION ALL
SELECT 2, TIMESTAMP '2021-03-11 01:00:00', 101, 40, 2 FROM DUAL UNION ALL
SELECT 3, TIMESTAMP '2021-03-12 01:00:00', 101, 80, 1 FROM DUAL;
with i as (
select
/*Get total per product, supplier at each timestamp
to calculate running sum on timestamps without need to resolve ties with over(... rows between) addition*/
inv_timestamp
, product_id
, supplier_id
, sum(in_quantity) as quan
, sum(sum(in_quantity)) over(
partition by product_id
order by
inv_timestamp asc
, supplier_id asc
) as rsum
from INVENTORY_IN
group by
product_id
, supplier_id
, inv_timestamp
)
, o as (
select /*The same for customer*/
inv_timestamp
, product_id
, customer_id
, sum(out_quantity) as quan
, sum(sum(out_quantity)) over(
partition by product_id
order by
inv_timestamp asc
, customer_id asc
) as rsum
/*Last consumption per product: when lead goes beyond the current window*/
, lead(0, 1, 1) over(
partition by product_id
order by
inv_timestamp asc
, customer_id asc
) as last_consumption
from INVENTORY_OUT
group by
product_id
, customer_id
, inv_timestamp
)
, distr as (
select
/*Distribute the quantity. This is the basic interval intersection:
new_value_to = least(t1.value_to, t2.value_to)
new_value_from = greatest(t1.value_from, t2.value_from)
So we need a capacity of the interval
*/
i.product_id
, least(i.rsum, nvl(o.rsum, i.rsum))
- greatest(i.rsum - i.quan, nvl(o.rsum - o.quan, i.rsum - i.quan)) as supplied_quan
/*At the last supply we can have something not used.
Calculate it to add later as not consumed
*/
, case
when last_consumption = 1
and i.rsum > nvl(o.rsum, i.rsum)
then i.rsum - o.rsum
end as rest_quan
, i.supplier_id
, o.customer_id
, i.inv_timestamp as i_ts
, o.inv_timestamp as o_ts
from i
left join o
on i.product_id = o.product_id
/*No equality here, because values are continuous:
>= will include the same value in two intervals if some of value_to of one table equals
another's table value_to (which is value_from for the next interval)*/
and i.rsum > o.rsum - o.quan
and o.rsum > i.rsum - i.quan
)
select
product_id
, supplier_id
, customer_id
, sum(quan) as quan
from (
select /*Get distributed quantities*/
product_id
, supplier_id
, customer_id
, supplied_quan as quan
, i_ts
, o_ts
from distr
union all
select /*Add not consumed part of last consumed supply*/
product_id
, supplier_id
, null
, rest_quan
, i_ts
, null /*No consumption*/
from distr
where rest_quan is not null
)
group by
product_id
, supplier_id
, customer_id
order by
min(i_ts) asc
/*To order not consumed last*/
, min(o_ts) asc nulls last
PRODUCT_ID | SUPPLIER_ID | CUSTOMER_ID | QUAN
---------: | ----------: | ----------: | ---:
101 | 0 | 1 | 20
101 | 4 | 1 | 60
101 | 4 | 2 | 40
101 | 3 | 1 | 30
101 | 3 | null | 20
101 | 2 | null | 10
db<>fiddle here

Last Changed Date

ID DATE AMT
A 20180401 110
A 20180301 110
A 20180201 100
A 20171010 90
B 20181001 90
B 20180901 90
B 20180707 80
My Output should be
ID DATE AMT Result
A 20180401 110 20180201
A 20180301 110 20180201
A 20180201 100 20171010
A 20171010 90 null
B 20181001 90 20180707
B 20180901 90 20180707
B 20180707 80 null
So i need to get the result column date of Last value different from current value with in same ID
so if we take the first record in this case current AMT value is 110 and next record also has 110 and the next record is 100 which is different from current value so I need to get that date -
I have used
LAST_VALUE ( DATE) OVER ( PARTITION BY ID, AMT ORDER BY ID ) AS LASTVALUE -I'm getting the date for the records with same Amount
This is after the
LAST_VALUE ( DATE) OVER ( PARTITION BY ID, AMT ORDER BY ID ) AS LASTVALUE2
ID;DAT;AMT;LASTVALUE2 -After Last Value
A;Mar 1, 2018;130;Mar 1, 2018
A;Feb 1, 2018;110;Jan 1, 2018
A;Jan 1, 2018;110;Jan 1, 2018
A;Nov 1, 2017;140;Nov 1, 2017
B;Jun 1, 2018;110;Apr 1, 2018
B;May 1, 2018;110;Apr 1, 2018
B;Apr 1, 2018;110;Apr 1, 2018
B;Mar 1, 2018;130;Mar 1, 2018
ID;DAT;AMT;PREV_DIFF_VALUE -After Lag
A;Nov 1, 2017;140;?
A;Jan 1, 2018;110;Nov 1, 2017
A;Feb 1, 2018;110;Jan 1, 2018
A;Mar 1, 2018;130;Feb 1, 2018
B;Mar 1, 2018;130;?
B;Apr 1, 2018;110;Mar 1, 2018
B;May 1, 2018;110;Apr 1, 2018
B;Jun 1, 2018;110;May 1, 2018
The third record should be Nov 1 2017
Thanks in advance
This is tricky. I think this does what you want:
select t.*,
max(case when result <> next_result then date end) over (partition by id order by date rows between unbounded preceding and 1 preceding)
from (select t.*,
lead(result) over (partition by a order by b) as next_result
from t
) t;
Try:
SELECT s1.ID
, FORMAT(s1.theDate,'MM-dd-yyyy') AS theDate
, s1.Amt
--, s1.PrevAmt
, CASE
WHEN Amt <> prevAmt
THEN FORMAT(
LAG(theDate) OVER ( PARTITION BY ID ORDER BY theDate )
,'MM-dd-yyyy' )
END AS prevDate
FROM (
SELECT ID, theDate, Amt
, LAG(AMT) OVER ( PARTITION BY ID ORDER BY theDate) AS prevAmt
FROM t1
) s1
ORDER BY ID, theDate DESC
This should give:
ID | theDate | Amt | prevDate
:- | :--------- | --: | :---------
A | 10-10-2017 | 90 | null
A | 04-04-2018 | 110 | null
A | 03-03-2018 | 110 | 02-02-2018
A | 02-02-2018 | 100 | 10-10-2017
B | 10-10-2018 | 90 | null
B | 09-09-2018 | 90 | 07-07-2018
B | 07-07-2018 | 80 | null
db<>fiddle here
For rows that don't have a previous row to pull the date from, it will return a NULL in the prevDate field.

SQL - running total when data already grouped

I am trying to do a running total for some data, and have seen the easy way to do it. However, I have already grouped some data and this is throwing off my code. I currently have dates and payment types, and the totals that it relates to.
What I have at the moment is:
create table #testdata
(
mdate date,
pmttype varchar(64),
totalpmtamt int
)
insert into #testdata
select getdate()-7, 'DD', 10
union
select getdate() -7, 'SO', 12
union
select getdate()-6, 'DD', 3
union
select getdate()-5, 'DD', 13
union
select getdate()-5, 'SO', 23
union
select getdate()-5, 'PO', 8
What I want to have is:
mdate | paymenttype | totalpmtamt | incrtotal
2016-08-29 | DD | 10 | 10
2016-08-29 | SO | 12 | 22
2016-08-30 | DD | 3 | 25
2016-08-31 | DD | 13 | 38
2016-08-31 | SO | 8 | 46
2016-08-31 | PO | 23 | 69
I've tried adapting other code I've found here into:
select t1.mdate,
t1.pmttype,
t1.totalpmtamt,
SUM(t2.totalpmtamt) as runningsum
from #testdata t1
join #testdata t2 on t1.mdate >= t2.mdate and t1.pmttype >= t2.pmttype
group by t1.mdate, t1.pmttype, t1.totalpmtamt
order by t1.mdate
but all I get is
mdate | paymenttype | totalpmtamt | incrtotal
2016-08-29 | DD | 10 | 10
2016-08-29 | SO | 12 | 22
2016-08-30 | DD | 3 | 13
2016-08-31 | DD | 13 | 26
2016-08-31 | SO | 8 | 34
2016-08-31 | PO | 23 | 69
Can anyone help please?
The ANSI standard way of doing a cumulative sum is:
select t.*, sum(totalpmtamt) over (order by mdate) as runningsum
from #testdata t
order by t.mdate;
Not all databases support this functionality.
If your database doesn't support that functionality, I would go for a correlated subquery:
select t.*,
(select sum(t2.totalpmtamt)
from #testdata t2
where t2.mdate <= t.mdate
) as runningsum
from #testdata
order by t.mdate;
Use the below query for the desired result (for SQL Server).
with cte_1
as
(SELECT *,ROW_NUMBER() OVER(order by mdate ) RNO
FROM #testdata)
SELECT mdate,pmttype,totalpmtamt,(select sum(c2.totalpmtamt)
from cte_1 c2
where c2.RNO <= c1.RNO
) as incrtotal
FROM cte_1 c1
Output :
Sounds like SQL Server.
DECLARE #testdata TABLE
(
mdate DATE ,
pmttype VARCHAR(64) ,
totalpmtamt INT
);
INSERT INTO #testdata
( mdate, pmttype, totalpmtamt )
VALUES ( GETDATE() - 7, 'DD', 10 ),
( GETDATE() - 7, 'SO', 12 ),
( GETDATE() - 6, 'DD', 3 ),
( GETDATE() - 5, 'DD', 13 ),
( GETDATE() - 5, 'SO', 23 ),
( GETDATE() - 5, 'PO', 8 );
SELECT *,
SUM(totalpmtamt) OVER ( ORDER BY mdate ROWS UNBOUNDED PRECEDING )
AS RunningTotal
FROM #testdata t;

How To get the First Row Form SQL Group Query?

I have a problem in writing a query.
I'd like to select the first row of each set of rows grouped
My table is Transactions:
userID | Date | StoreID
---------------------------
1 | 8-9-2013 | 10
1 | 9-9-2013 | 10
1 | 10-9-2013| 20
2 | 7-9-2013 | 30
2 | 8-9-2013 | 10
2 | 9-9-2013 | 20
1 | 11-9-2013| 10
2 | 10-9-2013| 20
and I try to this SQL statement:
Select
tr.userID , Min(tr.TransactionDate) FirstDate
From
Transactions tr
Group By
tr.userID
I get this output:
userID | Date
------------------
1 | 8-9-2013
2 | 7-9-2013
But I need the Store ID in every first transaction.
I need it to be like that
userID | Date | StoreID
-------------------------
1 | 8-9-2013 | 10
2 | 7-9-2013 | 30
Please any one can help me
You could use Row_Number().
select UserId, Date, StoreId from (select row_number() over(partition
by UserId order by date) as RowNumber, UserId, Date, StoreId from
Transactions ) as View1 where RowNumber = 1
http://sqlfiddle.com/#!6/e536a/7
You could use a sub-query
SELECT TR1.userID
,TR1.TransactionDate
,TR1.StoreID
FROM Transactions tr1
INNER JOIN
(
Select
tr.userID
,Min(tr.TransactionDate) AS FirstDate
From
Transactions tr
Group By
tr.userID
) SQ
ON TR1.userID = SQ.userID
AND TR1.TransactionDate = SQ.FirstDate
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Transactions
([userID] int, [Date] datetime, [StoreID] int)
;
INSERT INTO Transactions
([userID], [Date], [StoreID])
VALUES
(1, '2013-08-09 00:00:00', 10),
(1, '2013-09-09 00:00:00', 10),
(1, '2013-10-09 00:00:00', 20),
(2, '2013-07-09 00:00:00', 30),
(2, '2013-08-09 00:00:00', 10),
(2, '2013-09-09 00:00:00', 20),
(1, '2013-11-09 00:00:00', 10),
(2, '2013-10-09 00:00:00', 20)
;
Query 1:
SELECT
tr.userID , Min(tr.Date) FirstDate , tr2.storeid
FROM
Transactions tr
inner join Transactions tr2 on tr.userid = tr2.userid and
tr2.date = (select top 1 date
from transactions t
where t.userid = tr2.userid
order by date asc)
GROUP BY
tr.userID, tr2.storeid
Results:
| USERID | FIRSTDATE | STOREID |
|--------|-------------------------------|---------|
| 1 | August, 09 2013 00:00:00+0000 | 10 |
| 2 | July, 09 2013 00:00:00+0000 | 30 |
with user_cte (userid,date)
as(Select tr.userID , Min(tr.TransactionDate) FirstDate
From Transactions tr
Group By tr.userID
)
select b.userid,b.date,a.storeId from Transactions a join user_cte b on a.userID=b.userId and a.Date=b.Date
You can do this with a subquery. The basic principle is that the subquery identifies the min date for each one, and the wrapping query picks the row that matches the user and min date, also being able to return the store id.
It would look something like this:
SELECT
t.UserID,
t.Date,
t.StoreId
FROM
Transactions t JOIN
(
SELECT
tr.userID , Min(tr.Date) AS FirstDate
FROM
Transactions tr
GROUP BY
tr.userID
) u ON t.UserId = u.UserId AND t.Date = u.FirstDate
You can check this out yourself in SqlFiddle here.