I have table a auctions and table b bids:
Table a (Auctions):
id status
1 1
2 0
3 0
4 1
Table b (Bids):
id auction_id amount date
1 1 0.1 2018-01-24
2 1 0.1 2018-01-24
3 4 0.2 2018-01-26
4 4 0.1 2018-01-26
5 4 0.3 2018-01-28
6 1 0.1 2018-01-29
What I want to get is the amount of bids, the total sum of bids, the last bid date per auction from table b in addition to their id and status from table a everything ordered by status:
Something like this:
id status bid_sum bid_count last_bid_date
4 1 0.6 3 2018-01-28
1 1 0.3 3 2018-01-29
3 0 0 0 0000-00-00
2 0 0 0 0000-00-00
So far I have this query:
SELECT a.id, a.status, SUM( b.amount ) as bids_sum, COUNT( b.id ) as bids_count, MAX( b.dt ) as last_bid_date
FROM a, b
GROUP BY a.id
ORDER BY a.status DESC, a.id DESC
The problem is that it is returning only auctions that have at least 1 bid
id status bid_sum bid_count last_bid_date
4 1 0.6 3 2018-01-28
1 1 0.3 3 2018-01-29
And I need all auctions. Can you please help? Thank you!
SELECT
a.id,
a.status,
COALESCE(SUM(b.amount), 0) as bids_sum,
COUNT(b.id) as bids_count,
COALESCE(MAX(b.dt), '0000-00-00') as last_bid_date
FROM a left outer join b
on a.id = b.auction_id
GROUP BY a.id
ORDER BY a.status DESC, a.id DESC
What you want to do is a left join:
SELECT a.id,
a.status,
SUM(b.amount) AS bids_sum,
COUNT(b.id) AS bids_count,
MAX(b.dt) AS last_bid_date
FROM a
LEFT JOIN b ON a.id = b.auction_id
GROUP BY a.id
ORDER BY a.status DESC,
a.id DESC
Related
I'm currently stuck on how to create a statement that shows daily overdraft statements for a particular council.
I have the following, councils, users, markets, market_transactions, user_deposits.
market_transaction run daily reducing user's account balance. When the account_balance is 0 the users go into overdraft (negative). When users make a deposit their account balance increases.
I Have put the following tables to show how transactions and deposits are stored.
if I reverse today's transactions I'm able to get what account balance a user had yesterday but to formulate a query to get the daily OD amount is where the problem is.
USERS
user_id
name
account_bal
1
Wells
-5
2
James
100
3
Joy
10
4
Mumbi
-300
DEPOSITS
id
user_id
amount
date
1
1
5
2021-04-26
2
3
10
2021-04-26
3
3
5
2021-04-25
4
4
5
2021-04-25
TRANSACTIONS
id
user_id
amount_tendered
date
1
1
5
2021-04-27
2
2
10
2021-04-26
3
3
15
2021-04-26
4
4
50
2021-04-25
The Relationships are as follows,
COUNCILS
council_id
name
1
a
2
b
3
c
MARKETS
market_id
name
council_id
1
x
3
2
y
1
3
z
2
MARTKET_USER_LINK
id
market_id
user_id
1
1
3
2
2
2
3
3
1
I'm running this SQL query to get the total amount users have spent and subtracting with the current user account balance.
Don't know If I can use this to figure out the account_balance for each day.
SELECT u.user_id, total_spent, total_deposits,m.council_id
FROM users u
JOIN market_user_link ul ON ul.user_id= u.user_id
LEFT JOIN markets m ON ul.market_id =m.market_id
LEFT JOIN councils c ON m.council_id =c.council_id
LEFT JOIN (
SELECT user_id, SUM(amount_tendered) AS total_spent
FROM transactions
WHERE DATE(date) BETWEEN DATE('2021-02-01') AND DATE(NOW())
GROUP BY user_id
) t ON t.user_id= u.user_id
ORDER BY user_id, total_spent ASC
// looks like this when run
| user_id | total_spent | council_id |
|-------------|----------------|------------|
| 1 | 50.00 | 1 |
| 2 | 2.00 | 3 |
I was hoping to reverse transactions and deposits done to get the account balance for a day then get the sum of users with an account balance < 0... But this has just failed to work.
The goal is to produce a query that shows daily overdraft (Only SUM the total account balance of users with account balance below 0 ) for a particular council.
Expected Result
date
council_id
o_d_amount
2021-04-24
1
-300.00
2021-04-24
2
-60.00
2021-04-24
3
-900.00
2021-04-25
1
-600.00
2021-04-25
2
-100.00
2021-04-25
3
-1200.00
This is actually not that hard, but the way you asked makes it hard to follow.
Also, your expected result should match the data you provided.
Edited: Previous solution was wrong - It counted withdraws and deposits more than once if you have more than one event for each user/date.
Start by having the total exchanged on each day, like
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d
group by user_id, date
order by user_id, date;
What follows gets the state of the account only on days that had any deposits or withdraws.
To get the results of all days (and not just those with account movement) you just have to change the cross join part to get a table with all dates you want (like Get all dates between two dates in SQL Server) but I digress...
select dates.date, c.council_id, u.name username
, u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
, u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
from councils c
inner join markets m on c.council_id=m.council_id
inner join market_user_link mul on m.market_id=mul.market_id
inner join users u on mul.user_id=u.user_id
left join (
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d group by user_id, date
) e on u.user_id=e.user_id --exchange on each Day
cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
group by dates.date, c.council_id, u.name, u.account_bal
order by dates.date desc, c.council_id, u.name;
From there you can rearrange to get the result you want.
select date, council_id
, sum(case when amount_on_start_of_day<0 then amount_on_start_of_day else 0 end) o_d_amount_start
, sum(case when amount_on_end_of_day<0 then amount_on_end_of_day else 0 end) o_d_amount_end
from (
select dates.date, c.council_id, u.name username
, u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
, u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
from councils c
inner join markets m on c.council_id=m.council_id
inner join market_user_link mul on m.market_id=mul.market_id
inner join users u on mul.user_id=u.user_id
left join (
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d group by user_id, date
) e on u.user_id=e.user_id --exchange on each Day
cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
group by dates.date, c.council_id, u.name, u.account_bal
) result
group by date, council_id
order by date;
You can check it on https://www.db-fiddle.com/f/msScT6B5F7FjU2aQXVr2da/6
Basically the query maps users to councils, caculates periods of overdrafts for users, them aggregates over councils. I assume that starting balance is dated start of the month '2021-04-01' (it could be ending balance as well, see below), change it as needed. Also that negative starting balance counts as an overdraft. For simplicity and debugging the query is divided into a number of steps.
with uc as (
select distinct m.council_id, mul.user_id
from markets m
join market_user_link mul on m.market_id = mul.market_id
),
user_running_total as (
select user_id, date,
coalesce(lead(date) over(partition by user_id order by date) - interval 1 day, date) nxt,
sum(sum(s)) over(partition by user_id order by date) rt
from (
select user_id, date, -amount_tendered s
from transactions
union all
select user_id, date, amount
from deposits
union all
select user_id, se.d, se.s
from users
cross join lateral (
select date(NOW() + interval 1 day) d, 0 s
union all
select '2021-04-01' d, account_bal
) se
) t
group by user_id, date
),
user_overdraft as (
select user_id, date, nxt, least(rt, 0) ovd
from user_running_total
where date <= date(NOW())
),
dates as (
select date
from user_overdraft
union
select nxt
from user_overdraft
),
council__overdraft as (
select uc.council_id, d.date, sum(uo.ovd) total_overdraft, lag(sum(uo.ovd), 1, sum(uo.ovd) - 1) over(partition by uc.council_id order by d.date) prev_ovd
from uc
cross join dates d
join user_overdraft uo on uc.user_id = uo.user_id and d.date between uo.date and uo.nxt
group by uc.council_id, d.date
)
select council_id, date, total_overdraft
from council__overdraft
where total_overdraft <> prev_ovd
order by date, council_id
Really council__overdraft is quite usable, the last step just compacts output excluding intermidiate dates when overdraft is not changed.
With following sample data:
users
user_id name account_bal
1 Wells -5
2 James 100
3 Joy 10
4 Mumbi -300
deposits, odered by date, extra row added for the last date
id user_id amount date
3 3 5 2021-04-25
4 4 5 2021-04-25
1 1 5 2021-04-26
2 3 10 2021-04-26
5 3 73 2021-05-06
transactions, odered by date (note the added row, to illustrate running total in action)
id user_id amount_tendered date
5 4 50 2021-04-25
2 2 10 2021-04-26
3 3 15 2021-04-26
1 1 5 2021-04-27
4 3 17 2021-04-27
councils
council_id name
1 a
2 b
3 c
markets
market_id name council_id
1 x 3
2 y 1
3 z 2
market_user_link
id market_id user_id
1 1 3
2 2 2
3 3 1
4 3 4
the query ouput is
council_id
date
overdraft
1
2021-04-01
0
2
2021-04-01
-305
3
2021-04-01
0
2
2021-04-25
-350
2
2021-04-26
-345
2
2021-04-27
-350
3
2021-04-27
-7
3
2021-05-06
0
Alternatively, provided the users table is holding a closing (NOW()) balance, replace user_running_total CTE with the following code
user_running_total as (
select user_id, date,
coalesce(lead(date) over(partition by user_id order by date) - interval 1 day, date) nxt,
coalesce(sum(sum(s)) over(partition by user_id order by date desc
rows between unbounded preceding and 1 preceding), sum(s)) rt
from (
select user_id, date, amount_tendered s
from transactions
union all
select user_id, date, -amount
from deposits
union all
select user_id, se.d, se.s
from users
cross join lateral (
select date(NOW() + interval 1 day) d, account_bal s
union all
select '2021-04-01' d, 0
) se
) t
where DATE(date) between date '2021-04-01' and date(NOW() + interval 1 day)
group by user_id, date
),
This way the query starts with closing balance dated next date after now and rollouts a running total in the reverse order till '2021-04-01' as a starting date.
Output
council_id
date
overdraft
1
2021-04-01
0
2
2021-04-01
-260
3
2021-04-01
-46
2
2021-04-25
-305
3
2021-04-25
-41
2
2021-04-26
-300
3
2021-04-26
-46
2
2021-04-27
-305
3
2021-04-27
-63
3
2021-05-06
0
db-fiddle both versions
Below is my table let's call account
**ID accountID score tracking_date
1 1 3 2014-09-25 00:01:05
2 2 4 2014-09-26 01:05:18
3 1 6 2014-09-27 09:23:05
4 2 9 2014-09-28 20:01:05
5 1 1 2014-09-28 23:21:34
6 3 7 2014-09-21 00:01:00
7 2 1 2014-09-22 01:45:24
8 2 9 2014-09-27 14:01:43
9 3 1 2014-09-24 22:01:27
I want to select record with max date and also the difference of score with the records having tracking_date as minimum for that accountId. So I want output like below
ID accountID score_with_maxdate diff_score_with_mindate max_tracking_date
1 1 1 -2 2014-09-28 23:21:34
2 2 9 8 2014-09-28 20:01:05
3 3 1 -6 2014-09-24 22:01:27
Any help?
Here is one option. We can self-join a subquery which finds both the min and max tracking dates, for each account, twice to your original table. This will bring in all metadata for those max tracking date records, including the scores.
SELECT
t1.accountID,
t2.score AS score_with_maxdate,
t2.score - t3.score AS diff_score_with_mindate,
t1.max_tracking_date
FROM
(
SELECT
accountID,
MAX(tracking_date) AS max_tracking_date,
MIN(tracking_date) AS min_tracking_date
FROM yourTable
GROUP BY accountID
) t1
INNER JOIN yourTable t2
ON t1.accountId = t2.accountID AND t2.tracking_date = t1.max_tracking_date
INNER JOIN yourTable t3
ON t1.accountId = t3.accountID AND t3.tracking_date = t1.min_tracking_date
ORDER BY
t1.accountID;
Demo
This is a somewhat tricky question. I think conditional aggregation is a convenient way to solve the problem:
select min(t.id) as id, t.accountId,
max(case when t.tracking_date = t2.max_td then t.score end) as score_with_maxdate,
max(case when t.tracking_date = t2.max_td then t.score
when t.tracking_date = t2.min_td then - t.score
end) as diff_score_with_mindate,
max(t.tracking_date) as max_tracking_date
from t join
(select t2.accountId, min(t2.tracking_date) as min_td, max(t2.tracking_date) as max_td
from t t2
group by t2.accountId
) t2
on t.accountId = t2.accountId
group by t.accountId;
Another hackish way of getting same results by using aggregate and string fucntion
select t.accountID,
t.score_with_maxdate,
t.score_with_maxdate - t.score_with_mindate score_with_maxdate,
t.max_tracking_date
from(
select accountID,
substring_index(group_concat(score order by tracking_date desc),',', 1) + 0 score_with_maxdate,
substring_index(group_concat(score order by tracking_date asc),',', 1) + 0 score_with_mindate,
max(tracking_date) max_tracking_date
from demo
group by accountID
) t
Demo
But i would suggest you to go with other solutions mentioned by Tim & Gordon
I am looking to generate the results into the horizontal union. I could able to combine together and generate the results. However, I need to show the results in the expected results format.
SQL
(SELECT
kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM
pmm_question_details
WHERE
weightage NOT LIKE '%-%'
GROUP BY kpa_id) UNION (SELECT
kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM
pmm_answer_details
WHERE
application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id)
Actual Results
1 14
2 37
3 19
4 40
5 51
6 24
1 12
2 19
3 0
6 2
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40 6 2
5 51
6 24
If we can assume pmm_question_Details will always have as many or more records than pmm_answer_details... then two subqueries and a left join should do the trick with a join on a uservariable rownum (RN)
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA'
, SUM(weightage) AS 'Total'
, #RN1 := #RN1 + 1 as RN
FROM pmm_question_details
CROSS JOIN (SELECT #RN1 :=0) r1
WHERE weightage NOT LIKE '%-%'
ORDER BY KPA
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA'
, SUM(weightage_value) AS 'Acheived'
, #RN1 := #RN2 + 1 as RN
FROM pmm_answer_details
CROSS JOIN (SELECT #RN2 :=0) r2
WHERE application_id = 2
AND archive_value = 'No'
ORDER BY KPA
GROUP BY kpa_id) B
on A.RN = B.RN
ORDER BY A.KPA
Though I must admit I don't see why a rownumber is needed if you could just left join on the KPA_ID in the first place...
if this could be the Expected results... (and again assuming pmm_question has all the IDs which could be in pmm_answer... )
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40
5 51
6 24 6 2
Then the query would just be...
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM pmm_question_details
WHERE weightage NOT LIKE '%-%'
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM pmm_answer_details
WHERE application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id) B
on A.KPA = B.KPA
I have a table like following columns:
id date price pid
---------------------------
1 2015-02-01 34 2
2 2015-02-02 34 2
3 2015-02-03 34 2
4 2015-02-04 78 2
5 2015-02-05 78 2
6 2015-02-06 78 2
7 2015-02-07 52 2
8 2015-02-08 52 2
9 2015-02-09 52 2
10 2015-02-10 34 2
11 2015-02-11 34 2
12 2015-02-12 34 2
Now I want following result:
date_from date_to price pid
-------------------------------------
2015-02-01 2015-02-03 34 2
2015-02-04 2015-02-06 78 2
2015-02-07 2015-02-09 52 2
2015-02-10 2015-02-12 34 2
IMPORTANT:
I don't want to group the price "34" in this case.
One solution i can think of using user defined variables also i assume the id part is set to auto_increment
select min(t1.date) date_from,
max(t1.date) date_to,
t1.price,
t1.pid
from (
select t.*,
#r:= case when #g = price then #r else #r + 1 end r,
#g:= price g
from test t
cross join (select #g:=null,#r:=0) t
order by id
) t1
group by t1.r
DEMO
Borrowing M Khalid Junaid's fiddle...
SELECT a.date date_from
, MIN(c.date) date_to
, price
, pid
FROM test a
LEFT
JOIN test b
ON b.pid = a.pid
AND b.price = a.price
AND b.id = a.id - 1
LEFT
JOIN test c
ON c.pid = a.pid
AND c.price = a.price
AND c.id >= a.id
LEFT
JOIN test d
ON d.pid = a.pid
AND d.price = a.price
AND d.id = c.id + 1
WHERE b.id IS NULL
AND c.id IS NOT NULL
AND d.id IS NULL
GROUP
BY a.id, a.price, a.pid;
http://sqlfiddle.com/#!2/478f9/6
Try below query, it will group by three records on basis of date and price
SELECT min(created) AS date_from, max(created) AS date_to, price, pid
FROM t1
GROUP BY price, floor(DATEDIFF("2015-02-01", created) / 3);
Try this in MySQL. Here app_date is Date in your question:
set #rownum=1;
select min(app_date),max(app_date),price,pid from
(select t.id1 id,t.app_date app_date,t.price1 price,t.pid pid,
case when (t.price1=t.price2 or t.price2 is null) then #rownum else #rownum:=#rownum+1 end temp_num from
(select a.id id1,b.id id2,a.app_date app_date,a.price price1,b.price price2,a.pid pid from
test a left outer join test b on a.id=b.id+1) t
order by id1) temp
group by price,pid,temp_num
order by min(app_date);
Have to show on specific count . Means i have to show max score value 4 times then less than that score to 3 times and so on
I have table like:-
ID Score
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 3
9 3
10 4
11 4
12 4
and I am expecting output like:-
Score
1
1
1
2
2
3
4
Try this:
SELECT Score
FROM
(SELECT a.Score, a.ID, count(*) as RN FROM TableName a
JOIN TableName b ON a.Score = b.Score AND a.ID >= b.ID
GROUP BY a.Score, a.ID) T
WHERE RN>1
Result:
SCORE
1
1
1
2
2
3
4
4
An example in SQL Fiddle.
Explanation:
In the inner query, we are selecting a row number partitioned by ID and order by score. And then we select Score from the inner query whose Row number is greater than 1 (inorder to omit 1 record).
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.marks = x.marks
AND y.id <= x.id
GROUP
BY x.marks
, x.id
HAVING COUNT(0) <= MAX(4-x.marks)
ORDER
BY marks DESC,id;