I got three tables with data for products:
One where the products are defined.
A second where is defined which store has what products with what priceing with a timestamp.
And a third with the stock in the store of the products that are assiged.
Table 1 - products:
prdoductID|Name
----------|-------
1 |banana
2 |apple
Table 2 - prices:
storeID | productID | price | timestamp
--------|-----------|-------|------------
1 | 1 | 5,90 | 2016-03-27 16:00:00
1 | 1 | 5,90 | 2016-03-27 17:00:00
2 | 1 | 5,00 | 2016-03-27 16:00:00
2 | 2 | 5,00 | 2016-03-27 16:00:00
2 | 2 | 5,90 | 2016-03-28 19:00:00
Table 3 - stocks:
storeID | productID | stock | timestamp
--------|-----------|-------|------------
1 | 1 | 50 | 2016-03-27 08:00:00
1 | 1 | 5 | 2016-03-27 17:00:00
2 | 1 | 60 | 2016-03-27 09:00:00
2 | 2 | 0 | 2016-03-27 16:00:00
2 | 2 | 55 | 2016-03-28 19:00:00
`
Now I want to get the current state of one store for example at time 2016-03-27 14:00:00.
My current query get's for me the data but for all timestamps. I just want to get the corresponding entrys with the recent timesamps for each product of this store with the storeID = x;
Note: The user is able to plan. So there could be entries with futre timestamps so just a MAX would not work.
Query:
SELECT price.*, prod.name, stock.timestamp as 'stockTimesamp',
stock.stock FROM prices price
LEFT OUTER JOIN products prod on price.productID = prod.productID and
price.timestamp=(SELECT MAX(price.timestamp) WHERE price.timestamp <= '2016-03-27 14:00:00' and price.productID = prod.productID)
LEFT OUTER JOIN stocks stock on price.storeID = best.storeID and price.productID = stock.productID and stock.timestamp=(SELECT MAX(best.timestamp) WHERE stock.timestamp <= '2016-03-27 14:00:00' and stock.productID = prod.productID and stock.productID = price.productID)
where price.storeID=21
ORDER BY stock.timestamp DESC
Hope you can help me out...
This is a troublesome data structure for this type of query. You can get the most recent timestamp per product for each table and then join back to get additional information:
select p.*,
(select max(timestamp)
from stocks s
where s.storeid = $storeid and s.productid = p.productid and
s.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp,
(select max(timestamp)
from prices pr
where pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp
from products p;
Then, join back to the original tables to get additional information:
select p.*, s.*, pr.*
from (select p.*,
(select max(timestamp)
from stocks s
where s.storeid = $storeid and s.productid = p.productid and
s.timestamp <= '2016-03-27 14:00:00'
) as stock_timestamp,
(select max(timestamp)
from prices pr
where pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp <= '2016-03-27 14:00:00'
) as price_timestamp
from products p
) p left join
stocks s
on s.storeid = $storeid and s.productid = p.productid and
s.timestamp = p.stock_timestamp left join
prices pr
on pr.storeid = $storeid and pr.productid = p.productid and
pr.timestamp = p.price_timestamp ;
I should note that your problem would be relatively trivial if you had an effective and end date on each record. That is a better way to store a slowly changing dimension.
Related
I am using 10.1.39-MariaDB - mariadb.org binary and I have the following table:
| id | date | ticker | close |
|-------|---------------------|--------|-----------|
| 39869 | 2019-09-18 00:00:00 | AAPL | 221.96160 |
| 39870 | 2019-09-17 00:00:00 | AAPL | 220.70000 |
| 39871 | 2019-09-16 00:00:00 | AAPL | 219.90000 |
| 39872 | 2019-09-13 00:00:00 | AAPL | 218.75000 |
| 39873 | 2019-09-12 00:00:00 | AAPL | 223.09000 |
| 39874 | 2019-09-11 00:00:00 | AAPL | 223.59000 |
| 39875 | 2019-09-10 00:00:00 | AAPL | 216.70000 |
I have two queries where I am calculating metrics for 52-weeks and the second query calculates aggregation metrics for 20-days:
52-weeks:
SELECT
Y.*
FROM
(
SELECT
MAX(CLOSE) AS week_52_High,
DATE AS week_52_High_date,
MIN(CLOSE) AS week_52_Low,
DATE AS week_52_Low_date,
AVG(CLOSE) AS week_52_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 52 WEEK AND ticker = "AAPL") Y
LEFT JOIN prices tmax52 ON
tmax52.date = Y.week_52_High_date AND tmax52.close = week_52_High
LEFT JOIN prices tmin52 ON
tmin52.date = Y.week_52_Low_date AND tmin52.close = week_52_Low
LEFT JOIN prices tavg52 ON
tavg52.close = week_52_Avg
20-days
SELECT
Y.*
FROM
(
SELECT
MAX(CLOSE) AS day_20_High,
DATE AS day_20_High_date,
MIN(CLOSE) AS day_20_Low,
DATE AS day_20_Low_date,
AVG(CLOSE) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 20 DAY AND ticker = "AAPL") Y
LEFT JOIN prices tmax20 ON
tmax20.date = Y.day_20_High_date AND tmax20.close = day_20_High
LEFT JOIN prices tmin20 ON
tmin20.date = Y.day_20_Low_date AND tmin20.close = day_20_Low
LEFT JOIN prices tavg20 ON
tavg20.close = day_20_Avg
Both queries calculate the min/max/avg for each close price and attach the days, when this happened.
Any suggestions how to merge these two queries to get everything in 1 output?
I appreciate your replies!
Your first problem is that your query won't actually work. The correct way to get the dates for the high and low values is as below. Note that it is meaningless to try and get a date for the average close as it's highly unlikely that the stock will have closed at that price.
SELECT
Y.day_20_High,
tmax20.DATE AS day_20_High_date,
Y.day_20_Low,
tmin20.DATE AS day_20_Low_date,
Y.day_20_Avg
FROM
(
SELECT
MAX(CLOSE) AS day_20_High,
MIN(CLOSE) AS day_20_Low,
ROUND(AVG(CLOSE),2) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= CURDATE() - INTERVAL 20 DAY AND ticker = "AAPL") Y
LEFT JOIN prices tmax20 ON tmax20.close = Y.day_20_High
LEFT JOIN prices tmin20 ON tmin20.close = Y.day_20_Low
Output (for my sample data)
day_20_High day_20_High_date day_20_Low day_20_Low_date day_20_Avg
107.50 2019-09-20 101.10 2019-09-10 104.05
Demo on dbfiddle
Having corrected the query, you can now just JOIN to the same query for 52-week data:
SELECT
Y20.day_20_High,
tmax20.DATE AS day_20_High_date,
Y20.day_20_Low,
tmin20.DATE AS day_20_Low_date,
Y20.day_20_Avg,
Y52.week_52_High,
tmax52.DATE AS week_52_High_date,
Y52.week_52_Low,
tmin52.DATE AS week_52_Low_date,
Y52.week_52_Avg
FROM ((
SELECT
MAX(CLOSE) AS day_20_High,
MIN(CLOSE) AS day_20_Low,
ROUND(AVG(CLOSE),2) AS day_20_Avg
FROM
`prices`
WHERE
DATE >= CURDATE() - INTERVAL 20 DAY AND ticker = "AAPL") Y20
LEFT JOIN prices tmax20 ON tmax20.close = Y20.day_20_High
LEFT JOIN prices tmin20 ON tmin20.close = Y20.day_20_Low)
JOIN ((
SELECT
MAX(CLOSE) AS week_52_High,
MIN(CLOSE) AS week_52_Low,
ROUND(AVG(CLOSE),2) AS week_52_Avg
FROM
`prices`
WHERE
DATE >= DATE(NOW()) - INTERVAL 52 WEEK AND ticker = "AAPL") Y52
LEFT JOIN prices tmax52 ON tmax52.close = Y52.week_52_High
LEFT JOIN prices tmin52 ON tmin52.close = Y52.week_52_Low)
Output (for my sample data)
day_20_High day_20_High_date day_20_Low day_20_Low_date day_20_Avg week_52_High week_52_High_date week_52_Low week_52_Low_date week_52_Avg
107.50 2019-09-20 101.10 2019-09-10 104.05 109.70 2019-08-24 100.00 2019-08-21 104.19
Demo on dbfiddle
If you want to merge your queries one after another:
with t as (
select 0 as id, 1 as v0, 2 as v1
union all select 1 as id, 1 as v0, 2 as v1
)
select *
from (
select t.*, 'weekly_report' as typ from t
union all
select t.*, 'monthly_report' as typ from t
) as t1
id | v0 | v1 | typ
-: | -: | -: | :-------------
0 | 1 | 2 | weekly_report
1 | 1 | 2 | weekly_report
0 | 1 | 2 | monthly_report
1 | 1 | 2 | monthly_report
db<>fiddle here
And to have your results column by column, do a join:
with t as (
select 0 as id, 1 as v0, 2 as v1
union all select 1 as id, 1 as v0, 2 as v1
)
select t0.id, t0.v0, t0.v1, t1.v0, t1.v1
from (
select * from t
) as t0
full join (
select * from t
) as t1 on t0.id = t1.id
id | v0 | v1 | v0 | v1
-: | -: | -: | -: | -:
0 | 1 | 2 | 1 | 2
1 | 1 | 2 | 1 | 2
db<>fiddle here
This is what my query generated.
LOANTYPE | TOTALBALANCE | STATUS |
--------------------|--------------|--------|
Conventional Loans | 52.84 | Active |
Conforming Loans | 45.55 | Active |
Non-Conforming Loans| 43.90 | Active |
Secured Loans | 42.73 | Active |
Unsecured Loans | 34.99 | Active |
Open-ended Loans | 11.99 | Active |
Close-ended Loans | 11.69 | Active |
The TOTALBALANCE column is the summation of both Active and Inactive accounts per LOANTYPE.
Here is my query
SELECT
product.LOANTYPE,
SUM(account.PRINCIPALBALANCE + account.INTERESTBALANCE) AS TOTALBALANCE,
IF(DATE_ADD(MAX(transaction.PAYMENTDATES),
INTERVAL 6 MONTH) > CURRENT_DATE(),
'Active',
'Innactive') AS LOANSTATUS
FROM
account
INNER JOIN
client ON account.ACCOUNTKEY = client.PRIMARYKEY
INNER JOIN
product ON account.PRODUCTKEY = product.PRIMARYKEY
INNER JOIN
transaction ON transaction.ACCOUNTKEY = loanaccount.PRIMARYKEY
WHERE
transaction.TYPE = 'REPAYMENT'
GROUP BY product.LOANTYPE
I would like to get the TOTALBALANCE of Active/Innactive accounts as well as how many are Active/Inactive per LOANTYPE like so.
LOANTYPE | ACTIVEBALANCE | ACTIVE# | INACTIVEBALANCE | INNACTIVE#
--------------------|---------------|---------|-----------------|------------
Conventional Loans | 35.23 | 2 | 17.61 | 1
Conforming Loans | 18.22 | 1 | 27.33 | 1
Non-Conforming Loans| 32.486 | 2 | 11.414 | 2
Secured Loans | 17.092 | 2 | 25.638 | 1
Unsecured Loans | 40.61 | 2 | 5.6112 | 1
Open-ended Loans | 7.194 | 1 | 4.796 | 1
Close-ended Loans | 6.4395 | 2 | 5.26 | 2
I added
AND DATE_ADD(MAX(transaction.PAYMENTDATES),INTERVAL 6 MONTH) > CURRENT_DATE() in WHERE Clause
to limit the results but it still give me error.
What would/should I modify in my query to make my idea happen.
Thank you for your time.
Use conditional aggregation. The basic idea below is that we use a CASE expression to conditionally take a sum of balance, or record count, depending on whether the records are classified as active or inactive.
SELECT
product.LOANTYPE,
SUM(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) > CURRENT_DATE()
THEN account.PRINCIPALBALANCE + account.INTERESTBALANCE
ELSE 0 END) AS ACTIVEBALANCE,
COUNT(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) > CURRENT_DATE()
THEN 1 END) AS ACTIVE_CNT,
SUM(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) <= CURRENT_DATE()
THEN account.PRINCIPALBALANCE + account.INTERESTBALANCE
ELSE 0 END) AS INACTIVEBALANCE,
COUNT(CASE WHEN DATE_ADD(t.PAYMENTDATES, INTERVAL 6 MONTH) <= CURRENT_DATE()
THEN 1 END) AS INACTIVE_CNT
FROM account a
INNER JOIN client c
ON a.ACCOUNTKEY = c.PRIMARYKEY
INNER JOIN product p
ON a.PRODUCTKEY = p.PRIMARYKEY
INNER JOIN
(
SELECT ACCOUNTKEY, MAX(PAYMENTDATES) AS PAYMENTDATES
FROM transaction
WHERE TYPE = 'REPAYMENT'
GROUP BY ACCOUNTKEY
) t
ON t.ACCOUNTKEY = a.PRIMARYKEY
GROUP BY
p.LOANTYPE;
Note that in your question you refer to a loanaccount table, but this table appears nowhere in the actual query. I have assumed that you intended this to refer to the account table.
How about this
SELECT product.loantype,
active.totalbalance AS ACTIVEBALANCE,
active.cnt AS ACTIVE,
incative.totalbalance AS INACTIVEBALANCE,
inactive.cnt AS INACTIVE
FROM product
left join (SELECT product.loantype,
SUM(account.principalbalance
+ account.interestbalance) AS TOTALBALANCE,
COUNT(1) AS cnt
FROM account
inner join client
ON account.accountkey = client.primarykey
inner join product
ON account.productkey = product.primarykey
WHERE TRANSACTION.TYPE = 'REPAYMENT'
AND Current_date() > (SELECT DATE_ADD(MAX(
TRANSACTION.paymentdates),
interval 6 month)
FROM TRANSACTION
WHERE TYPE = 'REPAYMENT'
AND TRANSACTION.accountkey
=
account.accountkey)
GROUP BY product.loantype) AS active
ON( product.loantype = active.loantype )
left join (SELECT product.loantype,
SUM(account.principalbalance
+ account.interestbalance) AS TOTALBALANCE,
COUNT(1) AS cnt
FROM account
inner join client
ON account.accountkey = client.primarykey
inner join product
ON account.productkey = product.primarykey
WHERE TRANSACTION.TYPE = 'REPAYMENT'
AND Current_date() < (SELECT DATE_ADD(MAX(
TRANSACTION.paymentdates),
interval 6 month)
FROM TRANSACTION
WHERE TYPE = 'REPAYMENT'
AND TRANSACTION.accountkey
=
account.accountkey)
GROUP BY product.loantype) AS inactive
ON( product.loantype = inactive.loantype )
I run this query:
SELECT stockcarddetail.id, stockcarddetail.date, stockcarddetail.quantity, stockcarddetail.pricePerItem
FROM Stockcard
LEFT JOIN staff
ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail
ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND stockcarddetail.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC
and get the result below:
id | date | quantity| pricePerItem
50 | 2017-10-15 | 10.00 | 10000.00
1 | 2017-10-18 | 20.00 | 10000.00
Then, I need to calculate the cumulative of quantity based on the order above, so I run this query:
SELECT a.*, #tot:=#tot + a.quantity FROM
(SELECT #tot:= 0)b
JOIN
(SELECT stockcarddetail.id, stockcarddetail.date, stockcarddetail.quantity, stockcarddetail.pricePerItem
FROM Stockcard
LEFT JOIN staff
ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail
ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND stockcarddetail.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC) a
Then I got this result:
id | date | quantity| pricePerItem | #tot
1 | 2017-10-18 | 20.00 | 10000.00 | 20
50 | 2017-10-15 | 10.00 | 10000.00 | 30
However, the result that I want is like this:
id | date | quantity| pricePerItem | #tot
50 | 2017-10-15 | 10.00 | 10000.00 | 10
1 | 2017-10-18 | 20.00 | 10000.00 | 30
How can I get the expected result?
EDIT
Simplified version of the problem can be found here: http://sqlfiddle.com/#!9/f6ad91/3
From what I understand from you, you want the cumulative total for each entry.
I suggest ditching the variable and relying on a subquery instead:
SELECT
scd.id,
scd.date,
scd.quantity,
scd.pricePerItem,
(SELECT SUM(scd1.quantity) FROM StockcardDetail AS scd1 WHERE scd1.stockcardId = scd.stockcardId AND scd1.date <= scd.date) AS total
FROM Stockcard
LEFT JOIN staff ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail AS scd ON scd.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h'
AND scd.quantity > 0
AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY scd.date ASC
The idea behind this is to make it select the sum of all entries prior (including the current one) for each entry.
As per my understanding, you should get the expected output from your query. But, you aren't getting your expected output, then other possible solution is (WITHOUT JOIN)
SET #tot:= 0;
SELECT
stockcarddetail.id,
stockcarddetail.date,
stockcarddetail.quantity,
stockcarddetail.pricePerItem,
#tot:=#tot + stockcarddetail.quantity as Total
FROM Stockcard
LEFT JOIN staff ON staff.branchId = stockcard.branchId
LEFT JOIN stockcarddetail ON stockcarddetail.stockcardId = stockcard.id
WHERE staff.username = 'jemmy.h' AND stockcarddetail.quantity > 0 AND stockcard.productId = '98924a5f-6afb-11e7-8dd4-2c56dcbcb038'
ORDER BY date ASC
i have three tables
customer
id | name
1 | john
orders
id | customer_id | date
1 | 1 | 2013-01-01
2 | 1 | 2013-02-01
3 | 2 | 2013-03-01
order_details
id | order_id | qty | cost
1 | 1 | 2 | 10
2 | 1 | 5 | 10
3 | 2 | 2 | 10
4 | 2 | 2 | 15
5 | 3 | 3 | 15
6 | 3 | 3 | 15
i need to select data so i can get the output for each order_id the summary of the order
sample output. I will query the database with a specific customer id
output
date | amount | qty | order_id
2013-01-01 | 70 | 7 | 1
2013-02-01 | 50 | 4 | 2
this is what i tried
SELECT
orders.id, orders.date,
SUM(order_details.qty * order_details.cost) AS amount,
SUM(order_details.qty) AS qty
FROM orders
LEFT OUTER JOIN order_details ON order_details.order_id=orders.id AND orders.customer_id = 1
GROUP BY orders.date
but this returns the same rows for all customers, only that the qty and cost dont hav values
Maybe
SELECT
orders.id, orders.date,
SUM(order_details.qty * order_details.cost) AS amount,
SUM(order_details.qty) AS qty
FROM orders
LEFT JOIN order_details ON order_details.order_id=orders.id
AND orders.customer_id = 1
GROUP BY orders.date
HAVING amount is not null AND qty is not null
SQL Fiddle
NOTE: In the following query, it is assumed that the dates are stored in the database as a string in the format specified in the OP. If they are actually stored as some type of date with time then you'll want to modify this query such that the time is truncated from the date so the date represents the whole day. You can use the date or date_format functions. But then you'll need to make sure that you modify the query appropriately so the group by and select clauses still work. I added this modification as comments inside the query.
select
o.date -- or date(o.date) as date
, sum(odtc.total_cost) as amount
, sum(odtc.qty) as qty
, o.order_id
from
orders o
inner join (
select
od.id
, od.order_id
, od.qty
, od.qty * od.cost as total_cost
from
order_details od
inner join orders _o on _o.id = od.order_id
where
_o.customer_id = :customer_id
group by
od.id
, od.order_id
, od.qty
, od.cost
) odtc on odtc.order_id = o.id
where
o.customer_id = :customer_id
group by
o.date -- or date(o.date)
, o.order_id
;
I don't think you want an outer join just a simple inner join on all 3 tables:
FROM orders, order_details, customer
WHERE orders.customer_id=customer.id
AND order_details.order_id=orders.id
Update #1: query gives me syntax error on Left Join line (running the query within the left join independently works perfectly though)
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance'
FROM MyTable b1
JOIN CustomerInfoTable c on c.id = b1.company_id
#Filter for Clients of particular brand, package and active status
where c.brand_id = 2 and c.status = 2 and c.package_id = 3
LEFT JOIN
(
SELECT b2.company_id, sum(b2.debit) as 'Current_Usage'
FROM MyTable b2
WHERE year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
)
b3 on b3.company_id = b1.company_id
group by b1.company_id;
Original Post:
I keep track of debits and credits in the same table. The table has the following schema:
| company_id | timestamp | credit | debit |
| 10 | MAY-25 | 100 | 000 |
| 11 | MAY-25 | 000 | 054 |
| 10 | MAY-28 | 000 | 040 |
| 12 | JUN-01 | 100 | 000 |
| 10 | JUN-25 | 150 | 000 |
| 10 | JUN-25 | 000 | 025 |
As my result, I want to to see:
| Grouped by: company_id | Balance* | Current_Usage (in June) |
| 10 | 185 | 25 |
| 12 | 100 | 0 |
| 11 | -54 | 0 |
Balance: Calculated by (sum(credit) - sum(debits))* - timestamp does not matter
Current_Usage: Calculated by sum(debits) - but only for debits in JUN.
The problem: If I filter by JUN timestamp right away, it does not calculate the balance of all time but only the balance of any transactions in June.
How can I calculate the current usage by month but the balance on all transactions in the table. I have everything working, except that it filters only the JUN results into the current usage calculation in my code:
SELECT b.company_id, ((sum(b.credit)-sum(b.debit))/1024/1024/1024/1024) as 'BW_remaining', sum(b.debit/1024/1024/1024/1024/28*30) as 'Usage_per_month'
FROM mytable b
#How to filter this only for the current_usage calculation?
WHERE month(a.timestamp) = 'JUN' and a.credit = 0
#Group by company in order to sum all entries for balance
group by b.company_id
order by b.balance desc;
what you will need here is a join with sub query which will filter based on month.
SELECT T1.company_id,
((sum(T1.credit)-sum(T1.debit))/1024/1024/1024/1024) as 'BW_remaining',
MAX(T3.DEBIT_PER_MONTH)
FROM MYTABLE T1
LEFT JOIN
(
SELECT T2.company_id, SUM(T2.debit) T3.DEBIT_PER_MONTH
FROM MYTABLE T2
WHERE month(T2.timestamp) = 'JUN'
GROUP BY T2.company_id
)
T3 ON T1.company_id-T3.company_id
GROUP BY T1.company_id
I havn't tested the query. The point here i am trying to make is how you can join your existing query to get usage per month.
alright, thanks to #Kshitij I got it working. In case somebody else is running into the same issue, this is how I solved it:
SELECT b1.company_id, ((sum(b1.credit)-sum(b1.debit)) as 'Balance',
(
SELECT sum(b2.debit)
FROM MYTABLE b2
WHERE b2.company_id = b1.company_id and year(b2.timestamp) = '2012' and month(b2.timestamp) = '06'
GROUP BY b2.company_id
) AS 'Usage_June'
FROM MYTABLE b1
#Group by company in order to add sum of all zones the company is using
group by b1.company_id
order by Usage_June desc;