I'm learning mysql and am having difficult time getting my head around more complicated outputs - mainly the logic part... I have a simple database that contains 2 tables with 1 connection - design is here https://prnt.sc/mfmwji
I need to create a report that displays daily balance of only negative states (so only if person is in a negative balance) for the past 6 months.
I've put together query that displays only differences when they're negative, but it does not 'connect' them to rows before them... only displays the withdraws so to say.
I've played around with query but this is the 'best' thing I've came up with... I've tried to wrap the difference with sum function but that just sums the whole thing and doesn't return daily difference.
SELECT
T1.name AS Name,
T2.withdraw - T2.deposit AS Difference,
DATE_FORMAT(T2.date, '%Y-%m-%d') AS Date
FROM
users AS T1
INNER JOIN transactions AS T2
ON
T1.id = T2.user_id
WHERE
(T2.withdraw - T2.deposit) > 0
The query returns this output (its just a part of result since I got 100 results)
http://prntscr.com/mfn0xf
The deposits and withraws for Pearl Champlin so you get the idea are:
http://prntscr.com/mfn15a
I've tried to check other questions on SO but they usually point to other problems and are not specific to my problem.
Thanks in advance for any information you think I should check out!
You could use a subquery to retrieve the balance up to date. Then, in an outer query, you can filter for where that balance is negative:
select *
from (
select u.name,
t.date,
t.deposit - t.withdraw action,
( select sum(deposit - withdraw)
from transactions
where user_id = u.id
and date <= t.date ) as balance
from users as u
inner join transactions as t
on u.id = t.user_id
) balances
where balance < 0
order by 1, 2
This is what you asked. It shows the report for one user. I don't know if there is a way to make this for all the user at the same time. Maybe it can help you to find what you want.
SELECT
PreAgg.name,
(PreAgg.withdraw - PreAgg.deposit) AS Difference,
#PrevBal := #PrevBal + (PreAgg.withdraw - PreAgg.deposit) AS Balance
FROM
(SELECT
T1.name,
T2.deposit,
T2.withdraw,
(T2.withdraw - T2.deposit) AS Difference,
T1.id
FROM
users AS T1
INNER JOIN transactions AS T2
ON
T1.id = T2.user_id
ORDER BY
T2.id ) AS PreAgg,
(SELECT #PrevBal := 0) as InitialVar
WHERE PreAgg.id = 1
Related
I have a PHP/MySQL application
The application uses a query to get the values of a table leads, with 2 sub-queries to return the SUM and COUNT of values in a second table refunds
The 2 tables are linked with a foreign key lead_id
SELECT l.*,
IFNULL(
(SELECT SUM(amount)
FROM refunds r
WHERE l.lead_id = r.lead_id),0) amount_refunded,
IFNULL(
(SELECT COUNT(*)
FROM refunds r
WHERE l.lead_id = r.lead_id),0) number_refunded
FROM leads l
I would like to increase the performance of this query.
My thought was to:
Combine the the 2 sub-queries into a single sub-query using CONCAT
with a pipe delimiter
Explode the returned string using PHP at the application level to
get the 2 values.
Example below:
SELECT l.*,
(SELECT CONCAT(IFNULL(COUNT(*),0),'|', IFNULL(SUM(amount),0))
FROM fee_refunds r
WHERE l.lead_id = r.lead_id) values_refunded
FROM fee_leads l
Then in the application, within the loop:
list($amount_refunded, $number_refunded) = explode('|', $row->values_refunded);
This approach works, however my questions are:
Is this bad form?
Is there any reason I should not do it this way?
Is there a better solution?
Use join!
SELECT l.*, r.amount_refunded, r.number_refunded
FROM leads l LEFT JOIN
(SELECT lead_id, COUNT(*) as number_refunded, SUM(amount) as amount_refunded
FROM refunds r
GROUP BY lead_id
) r
ON l.lead_id = r.lead_id;
You may find it faster, under some circumstances, to join before the aggregation.
I just want to ask if with this kind of database design below and query below will this have a big effect on the query performance or should I break down the query instead of multiple subqueries. Though this subquery works for me I just want to make sure that someday it will not affect the performance. My goal in mind with this query is that I want to generate a dynamic queries for all those table. Example query for this is to view list of participants attended in a specific commodity and start date between 2 given date.Thank you in advance.
Below is my sample code that will view participants in between 1 and 3 years old.
SELECT TT.title, TTP.*, TS.*, TP.lastname, TP.firstname, (DATE_FORMAT(NOW(), '%Y') - DATE_FORMAT(TP.birthday, '%Y') - (DATE_FORMAT(NOW(), '00-%m-%d') < DATE_FORMAT(TP.birthday, '00-%m-%d')) )AS age, TP.birthday
FROM tbl_trainingparticipant AS TTP
LEFT JOIN (
SELECT t1.*
FROM tbl_schedules t1
WHERE t1.sched_id = (
SELECT t2.sched_id
FROM tbl_schedules t2
WHERE t2.training_id = t1.training_id
ORDER BY t2.sched_id DESC LIMIT 1
)
)AS TS ON TTP.training_id = TS.training_id
LEFT JOIN tbl_participants AS TP
ON TTP.participant_id = TP.participant_id
LEFT JOIN tbl_trainings AS TT
ON TT.training_id = TS.training_id
HAVING age BETWEEN 1 AND 3
SQL has an ability to nest queries within one another. A subquery is a
SELECT statement that is nested within another SELECT statement and
which return intermediate results. SQL executes innermost subquery
first, then next level.
So if you use a nested select you assigned task to mysql to execute two queries which are sent to it together. Usually it's going to be faster to do one trip than several.
I have found a problem I can't solve by myself.
I have users in table AE_Users where I store their special Points
Additionaly I have AE_Event table where I store events the users can join to, and for these events I am storing play time in seconds (timestamp) which is taken care of by CRON script checking players on server.
There is another table named AE_EventJoin where user is stored when he joins one of the events (so there can be multiple records of one user joining multiple event IDs)
I am able to calculate one user's point count by using 3 SQLs in PHP script:
SELECT `SteamID` FROM AE_Users WHERE `SteamID` = $SteamID
SELECT SUM(`EventInfo`) FROM AE_Events WHERE ID IN (SELECT `EventID` FROM AE_EventJoin WHERE `JoinType` = 1 AND `SteamID` = '$SteamID')
SELECT `Points` FROM AE_Users WHERE `SteamID` = $SteamID
but now I need to get top 5 users with most points and I really couldn't solve how to query the DB to give them to me.
I don't want to query DB for all users and then sort them in PHP, that would be too ugly.
I believe there is a way on how to calculate the user points from single SQL query. I tried to build it myself, but it is not working as expected. I was able to come up with this:
SELECT T1.`SteamID`, T1.`Points` AS PointCount, SUM(T2.`EventInfo`) AS PointCount FROM AE_Users T1, AE_Events T2 WHERE T2.ID IN (SELECT `EventID` FROM AE_EventJoin WHERE `JoinType` = 1 AND `SteamID` = T1.`SteamID`) ORDER BY PointCount LIMIT 5
which returns this: phpMyAdmin result
and that is wrong. It return only one user and the first PointCount is user's special point count which is correct, but the second PointCount is totally wrong viz. picture
phpMyAdmin Event points
And I would like to merge those two variables into one PointCount but GROUP BY command didn't allow me to do it because of some error. It is not a big issue though, I can merge them in PHP easily.
So is there any way on how to get the information I need from tables with structure like I have?
You are missing a group by clause , so try this:
SELECT T1.`SteamID`, max(T1.`Points`) AS PointCount, SUM(T2.`EventInfo`) AS PointCount
FROM AE_Users T1
INNER JOIN AE_Events T2
ON T2.ID IN (SELECT `EventID` FROM AE_EventJoin
WHERE `JoinType` = 1
AND `SteamID` = T1.`SteamID`)
GROUP BY T1.`SteamID`
ORDER BY PointCount
LIMIT 5
Also, you used implicit(comma separated) join syntax, please try to avoid this and use the correct syntax of explicit joins like in my solution.
You didn't post your table structures so it was hard to guess what exactly do you need, but it is something similar to this.
This query can also be written with a join instead of IN()
SELECT T1.`SteamID`, max(T1.`Points`) AS PointCount, SUM(T2.`EventInfo`) AS PointCount
FROM AE_Users T1
INNER JOIN `EventID` T3
ON(t1.`steamID` = t3.`steamID` and `JoinType` = 1 AND `SteamID` = T1.`SteamID`)
INNER JOIN AE_Events T2
ON (T2.ID = T3.ID)
GROUP BY T1.`SteamID`
ORDER BY PointCount
LIMIT 5
I'm attempting to pull the latest pricing data from a table on an Inner Join. Prices get updated throughout the day but aren't necessary updated at midnight.
The following query works great when the data is updated on prices by the end of the day. But how do I get it to get yesterdays data if today's data is blank?
I'm indexing off of a column that is formatted like this date_itemnumber => 2015-05-22_12341234
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND concat('2015-05-23_',collection.itemid)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: .046 sec
To be clear, I want it to check for the most up to date record for that item. Regardless on if it is today, yesterday or before that.
SQLFiddle1
The following query gives me the desired results but with my production dataset it takes over 3 minutes to return results. As my dataset gets larger, it would take longer. So this can't be the most efficient way to do this.
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
AND (select history.date_itemid from history WHERE itemid=collection.itemid GROUP BY date_itemid DESC LIMIT 1)=history.date_itemid
WHERE h.description LIKE '%Awesome%'
Production Query time: 181.140 sec
SQLFiddle2
SELECT x.*
FROM history x
JOIN
( SELECT itemid
, MAX(date_itemid) max_date_itemid
FROM history
-- optional JOINS and WHERE here --
GROUP
BY itemid
) y
ON y.itemid = x.itemid
AND y.max_date_itemid = x.date_itemid;
http://sqlfiddle.com/#!9/975f5/13
This should works:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN(
SELECT a.*
FROM history a
INNER JOIN
( SELECT itemid,MAX(date_itemid) max_date_itemid
FROM history
GROUP BY itemid
) b ON b.itemid = a.itemid AND b.max_date_itemid = a.date_itemid
) AS history ON history.itemid = collection.itemid
WHERE h.description LIKE '%Awesome%'
I don't know if this take a lot of execution time. Please do try it, since you might have more data in your tables it will be a good test to see the query execution time.
This is actually a fairly common problem in SQL, at least I feel like I run into it a lot. What you want to do is join a one to many table, but only join to the latest or oldest record in that table.
The trick to this is to do a self LEFT join on the table with many records, specifying the foreign key and also that the id should be greater or less than the other records' ids (or dates or whatever you're using). Then in the WHERE conditions, you just add a condition that the left joined table has a NULL id - it wasn't able to be joined with a more recent record because it was the latest.
In your case the SQL should look something like this:
SELECT h.*, collection.*, history.price
FROM collection
INNER JOIN h ON collection.itemid=h.id
INNER JOIN history ON collection.itemid=history.itemid
-- left join history table again
LEFT JOIN history AS history2 ON history.itemid = history2.itemid AND history2.id > history.id
-- filter left join results to the most recent record
WHERE history2.id IS NULL
AND h.description LIKE '%Awesome%'
This is another approach that cuts one inner join statement
select h.*,his.date_itemid, his.price from history his
INNER JOIN h ON his.itemid=h.id
WHERE his.itemid IN (select itemid from collection) AND h.description LIKE '%Awesome%' and his.id IN (select max(id) from history group by history.itemid)
you can try it here http://sqlfiddle.com/#!9/837a8/1
I am not sure if this is what you want but i give it a try
EDIT: modified
CREATE VIEW LatestDatesforIds
AS
SELECT
MAX(`history`.`date_itemid`) AS `lastPriceDate`,
MAX(`history`.`id`) AS `matchingId`
FROM `history`
GROUP BY `history`.`itemid`;
CREATE VIEW MatchDatesToPrices
AS
SELECT
`ldi`.`lastPriceDate` AS `lastPriceDate`,
`ldi`.`matchingId` AS `matchingId`,
`h`.`id` AS `id`,
`h`.`itemid` AS `itemid`,
`h`.`price` AS `price`,
`h`.`date_itemid` AS `date_itemid`
FROM (`LatestDatesforIds` `ldi`
JOIN `history` `h`
ON ((`ldi`.`matchingId` = `h`.`id`)));
SELECT c.itemid,price,lastpriceDate,description
FROM collection c
INNER JOIN MatchDatesToPrices mp
ON c.itemid = mp.itemid
INNER JOIN h ON c.itemid = h.id
Difficult to test the speed on such a small dataset but avoiding 'Group By' might speed things up. You could try conditionally joining the history table to itself instead of Grouping?
e.g.
SELECT h.*, c.*, h1.price
FROM h
INNER JOIN history h1 ON h1.itemid = h.id
LEFT OUTER JOIN history h2 ON h2.itemid = h.id
AND h1.date_itemid < h2.date_itemid
INNER JOIN collection c ON c.itemid = h.id
WHERE h2.id IS NULL
AND h.description LIKE '%Awesome%'
Changing this line
AND h1.date_itemid < h2.date_itemid
to actually work on a sequential indexed field (preferably unique) will speed things up too. e.g. order by id ASC
I'm fairly new to MYSQL!
I need to make a SQL query where i check how many likes a row has (between two tables)
I found another question that looked like mine, but i can't get it to return anything (even though it doesn't create an error.
query:
SELECT *
FROM likes
INNER JOIN (SELECT likes.like_id,
COUNT(*) AS likes
FROM likes
INNER JOIN uploads ON likes.upload_id=uploads.upload_id
WHERE uploads.upload_date >= DATE_SUB(CURDATE(), INTERVAL 8 DAY)
GROUP BY uploads.upload_id) x ON x.like_id = likes.like_id
ORDER BY x.likes DESC
Link to the original question:
MySQL, Need to select rows that has the most frequent values in another table
Help is much appreciated
Kind regards,
Mathias
Since you didn't post your table structure I'll have to guess..
select someid, count(*) cnt from
(
select * from table1 t1 join table2 t2 on t1.someid = t2.someid
) as q0 group by someid order by cnt desc;
It will need tweaking to fit your schema.