The concerned schema and query is on SQL fiddle here http://sqlfiddle.com/#!2/312d3/16
The JOIN query below gets the result that I want.
SELECT p.*, payer.username as payer_username, payee.username as
payee_username FROM (`payments_history` p) JOIN (SELECT * FROM users)
AS payer ON `payer`.`user_id` = `p`.`payer_id` JOIN (SELECT * FROM
users) AS payee ON `payee`.`user_id` = `p`.`payee_id` ORDER BY
`p`.`created_timestamp`;
Doing a sum() on the "p.amount" column for all the rows works fine too.
SELECT SUM(p.amount) FROM (`payments_history` p) JOIN (SELECT * FROM
users) AS payer ON `payer`.`user_id` = `p`.`payer_id` JOIN (SELECT *
FROM users) AS payee ON `payee`.`user_id` = `p`.`payee_id` ORDER BY
`p`.`created_timestamp`;
But doing a sum() on the same column for the rows on each page (offset,limit) returns an empty result (I would like to have the total of the "amount" column for the rows on each page).
SELECT SUM(p.amount) FROM (`payments_history` p) JOIN (SELECT * FROM
users) AS payer ON `payer`.`user_id` = `p`.`payer_id` JOIN (SELECT *
FROM users) AS payee ON `payee`.`user_id` = `p`.`payee_id` ORDER BY
`p`.`created_timestamp` limit 0,2;
Also in the sum() query, when the offset starts from anything greater than 0 (try LIMIT 2,2), it returns an empty result.
What am I doing wrong?.
Thank you.
Give this a try:
SELECT SUM(l.amount) FROM (
SELECT p.amount FROM payments_history p
INNER JOIN users payer ON payer.user_id=p.payer_id
INNER JOIN users payee ON payee.user_id=p.payee_id
ORDER BY p.created_timestamp
LIMIT 0,10
) l
I'm not sure if you plan to add other fields to the query, or have further conditions in a WHERE clause, but having those joins in there doesn't seem all that useful right now.
Your question is just a little vague. What will be on the page that you want to have sums on? Is it a report for the specific user? If so, then you can just add a WHERE clause to your query to limit the sum to just that user. Something like:
SELECT SUM(p.amount)
FROM (`payments_history` p)
JOIN (SELECT * FROM users) AS payer ON `payer`.`user_id` = `p`.`payer_id`
JOIN (SELECT * FROM users) AS payee ON `payee`.`user_id` = `p`.`payee_id`
WHERE payer.user_id = [the user id]
ORDER BY `p`.`created_timestamp`;
If that's not what you want, then you may want to clarify the question a little bit. It's unclear.
Question:
Are you trying to get the sum of just one row? (your answer in the comments makes it seem that way) If so, that's just the value in the amount. You can't sum a single value.
Related
I'm trying to multiply the result of a subquery with a field from the 'main' query. See the following example:
Table: subscriptions
id
title
price
Table: users
subscription_id
SELECT
subscriptions.id,
subscriptions.title,
(select count(*) from users where users.subscription_id = subscriptions.id) AS qty
SUM(qty * subscriptions.price) AS total
FROM subscriptions
This gives the error Unknown column 'qty' in 'field list'. So it seems like the result from the subquery isn't available in the SELECT field. After searching StackOverflow I found some of the same questions and it seems I need to move the subquery from the select to a JOIN. This seems simple enough but I'm having trouble to modify my own query to work like this. Anyone who can push me in the right direction?
Don't put the subquery in the SELECT list, join with it.
SELECT s.id, s.title, u.qty, s.price * u.qty AS total
FROM subscriptions AS s
JOIN (
SELECT subscription_id, COUNT(*) AS qty
FROM users
GROUP BY subscription_id
) AS u ON s.id = u.subscription_id
Almost right.
SELECT
s.id,
s.title,
SUM(s.price * (select count(*) from users u where u.subscription_id = s.id)) AS total
FROM subscriptions s
GROUP BY s.id, s.title
I tried to reslove your query, check it
https://dbfiddle.uk/xrMrT7Y4
I don't know why someone has deleted my answer. Here I found issue in your query is you didn't group the aggregate function & If you are comparing ID then both tables should be considered. #Vinze
This is a slight variant of the question I asked here
SQL Query for getting maximum value from a column
I have a Person Table and an Activity Table with the following data
-- PERSON-----
------ACTIVITY------------
I have got this data in the database about users spending time on a particular activity.
I intend to get the data when every user has spent the maximum number of hours.
My Query is
Select p.Id as 'PersonId',
p.Name as 'Name',
act.HoursSpent as 'Hours Spent',
act.Date as 'Date'
From Person p
Left JOIN (Select MAX(HoursSpent), Date from Activity
Group By HoursSpent, Date) act
on act.personId = p.Id
but it is giving me all the rows for Person and not with the Maximum Numbers of Hours Spent.
This should be my result.
You have several issues with your query:
The subquery to get hours is aggregated by date, not person.
You don't have a way to bring in other columns from activity.
You can take this approach -- joins and group by, but it requires two joins:
select p.*, a.* -- the columns you want
from Person p left join
activity a
on a.personId = p.id left join
(select personid, max(HoursSpent) as max_hoursspent
from activity a
group by personid
) ma
on ma.personId = a.personId and
ma.max_hoursspent = a.hoursspent;
Note that this can return duplicates for a given person -- if there are ties for the maximum.
This is written more colloquially using row_number():
select p.*, a.* -- the columns you want
from Person p left join
(select a.*,
row_number() over (partition by a.personid order by a.hoursspent desc) as seqnum
from activity a
) a
on a.personId = p.id and a.seqnum = 1
ma.max_hoursspent = a.hoursspent;
I have a query.
SELECT * FROM users LEFT JOIN ranks ON ranks.minPosts <= users.postCount
This returns a row every time it is matched. By using a GROUP BY users.id I get each row as a individual id.
However, when they group I only get the first row. I would instead like the row with the highest value of ranks.minPosts
Is there a way to do this, also, would it be faster (less resources) to just use two different queries?
Assuming there is only one column in ranks that you want, you can do this using a correlated subquery:
SELECT u.*,
(select r.minPosts
from ranks r
where r.minPosts <= u.PostCount
order by minPosts desc
limit 1
) as minPosts
FROM users u;
If you need the entire row from ranks, then join it back in:
SELECT ur.*, r.*
FROM (SELECT u.*,
(select r.minPosts
from ranks r
where r.minPosts <= u.PostCount
order by minPosts desc
limit 1
) as minPosts
FROM users u
) ur join
ranks r
on ur.minPosts = r.minPosts;
(The * is for convenience; you should list out the columns you want.)
Because you're using mysql, this will work:
SELECT * FROM (
SELECT *, users.id user_id
FROM users
LEFT JOIN ranks ON ranks.minPosts <= users.postCount
ORDER BY ranks.minPosts DESC
) x
GROUP BY user_id
Mysql always returns the first row encountered for each unique group, so if you first order the data, then use the non-standard grouping behaviour, you'll get the row you want.
Disclaimer:
Although this works reliably in practice, the mysql documentation says not to rely on it. If you use this convenient approach (which will reliably pass any test you can write), you should consider that it is not recommended by mysql and that later releases of mysql may not continue behave in this way.
What we'd really like to do would be to order the rows by ranks.minPosts before the group by. Unfortunately MySQL doesn't support that without using a subquery of some form.
If the ranks are already ordered by their ids then you can extract the id by selecting MAX(ranks.id), and if they're not, you can still get the highest ranks.minPosts by selecting MAX(ranks.minPosts). However, it would be nice to be able to get the entire record. I guess you're left with the subquery solution, which is as follows:
SELECT <fields> FROM users LEFT JOIN
(SELECT * FROM ranks ORDER BY minPosts DESC) as r
ON r.minPosts <= users.postCount GROUP BY users.id
I have these tables and queries as defined in sqlfiddle.
First my problem was to group people showing LEFT JOINed visits rows with the newest year. That I solved using subquery.
Now my problem is that that subquery is not using INDEX defined on visits table. That is causing my query to run nearly indefinitely on tables with approx 15000 rows each.
Here's the query. The goal is to list every person once with his newest (by year) record in visits table.
Unfortunately on large tables it gets real sloooow because it's not using INDEX in subquery.
SELECT *
FROM people
LEFT JOIN (
SELECT *
FROM visits
ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id
Does anyone know how to force MySQL to use INDEX already defined on visits table?
Your query:
SELECT *
FROM people
LEFT JOIN (
SELECT *
FROM visits
ORDER BY visits.year DESC
) AS visits
ON people.id = visits.id_people
GROUP BY people.id;
First, is using non-standard SQL syntax (items appear in the SELECT list that are not part of the GROUP BY clause, are not aggregate functions and do not sepend on the grouping items). This can give indeterminate (semi-random) results.
Second, ( to avoid the indeterminate results) you have added an ORDER BY inside a subquery which (non-standard or not) is not documented anywhere in MySQL documentation that it should work as expected. So, it may be working now but it may not work in the not so distant future, when you upgrade to MySQL version X (where the optimizer will be clever enough to understand that ORDER BY inside a derived table is redundant and can be eliminated).
Try using this query:
SELECT
p.*, v.*
FROM
people AS p
LEFT JOIN
( SELECT
id_people
, MAX(year) AS year
FROM
visits
GROUP BY
id_people
) AS vm
JOIN
visits AS v
ON v.id_people = vm.id_people
AND v.year = vm.year
ON v.id_people = p.id;
The: SQL-fiddle
A compound index on (id_people, year) would help efficiency.
A different approach. It works fine if you limit the persons to a sensible limit (say 30) first and then join to the visits table:
SELECT
p.*, v.*
FROM
( SELECT *
FROM people
ORDER BY name
LIMIT 30
) AS p
LEFT JOIN
visits AS v
ON v.id_people = p.id
AND v.year =
( SELECT
year
FROM
visits
WHERE
id_people = p.id
ORDER BY
year DESC
LIMIT 1
)
ORDER BY name ;
Why do you have a subquery when all you need is a table name for joining?
It is also not obvious to me why your query has a GROUP BY clause in it. GROUP BY is ordinarily used with aggregate functions like MAX or COUNT, but you don't have those.
How about this? It may solve your problem.
SELECT people.id, people.name, MAX(visits.year) year
FROM people
JOIN visits ON people.id = visits.id_people
GROUP BY people.id, people.name
If you need to show the person, the most recent visit, and the note from the most recent visit, you're going to have to explicitly join the visits table again to the summary query (virtual table) like so.
SELECT a.id, a.name, a.year, v.note
FROM (
SELECT people.id, people.name, MAX(visits.year) year
FROM people
JOIN visits ON people.id = visits.id_people
GROUP BY people.id, people.name
)a
JOIN visits v ON (a.id = v.id_people and a.year = v.year)
Go fiddle: http://www.sqlfiddle.com/#!2/d67fc/20/0
If you need to show something for people that have never had a visit, you should try switching the JOIN items in my statement with LEFT JOIN.
As someone else wrote, an ORDER BY clause in a subquery is not standard, and generates unpredictable results. In your case it baffled the optimizer.
Edit: GROUP BY is a big hammer. Don't use it unless you need it. And, don't use it unless you use an aggregate function in the query.
Notice that if you have more than one row in visits for a person and the most recent year, this query will generate multiple rows for that person, one for each visit in that year. If you want just one row per person, and you DON'T need the note for the visit, then the first query will do the trick. If you have more than one visit for a person in a year, and you only need the latest one, you have to identify which row IS the latest one. Usually it will be the one with the highest ID number, but only you know that for sure. I added another person to your fiddle with that situation. http://www.sqlfiddle.com/#!2/4f644/2/0
This is complicated. But: if your visits.id numbers are automatically assigned and they are always in time order, you can simply report the highest visit id, and be guaranteed that you'll have the latest year. This will be a very efficient query.
SELECT p.id, p.name, v.year, v.note
FROM (
SELECT id_people, max(id) id
FROM visits
GROUP BY id_people
)m
JOIN people p ON (p.id = m.id_people)
JOIN visits v ON (m.id = v.id)
http://www.sqlfiddle.com/#!2/4f644/1/0 But this is not the way your example is set up. So you need another way to disambiguate your latest visit, so you just get one row per person. The only trick we have at our disposal is to use the largest id number.
So, we need to get a list of the visit.id numbers that are the latest ones, by this definition, from your tables. This query does that, with a MAX(year)...GROUP BY(id_people) nested inside a MAX(id)...GROUP BY(id_people) query.
SELECT v.id_people,
MAX(v.id) id
FROM (
SELECT id_people,
MAX(year) year
FROM visits
GROUP BY id_people
)p
JOIN visits v ON (p.id_people = v.id_people AND p.year = v.year)
GROUP BY v.id_people
The overall query (http://www.sqlfiddle.com/#!2/c2da2/1/0) is this.
SELECT p.id, p.name, v.year, v.note
FROM (
SELECT v.id_people,
MAX(v.id) id
FROM (
SELECT id_people,
MAX(year) year
FROM visits
GROUP BY id_people
)p
JOIN visits v ON ( p.id_people = v.id_people
AND p.year = v.year)
GROUP BY v.id_people
)m
JOIN people p ON (m.id_people = p.id)
JOIN visits v ON (m.id = v.id)
Disambiguation in SQL is a tricky business to learn, because it takes some time to wrap your head around the idea that there's no inherent order to rows in a DBMS.
I have something like this:
(SELECT COUNT(*) AS total FROM `transactions` WHERE
`asset`='u_{$user_id}' GROUP BY id)
UNION DISTINCT
(SELECT COUNT(*) AS total FROM transactions tr
INNER JOIN payments pa ON tr.asset = CONCAT('p_', pa.id)
WHERE pa.user_id = '{$user_id}'
GROUP BY tr.id)
It gives 1
Now works like this:
SELECT
(SELECT COUNT(*) FROM `transactions`
WHERE `asset`='u_{$user_id}')
+
(SELECT COUNT(*) FROM transactions tr
INNER JOIN payments pa ON tr.asset = CONCAT('p_', pa.id)
WHERE pa.user_id = '{$user_id}')
It gives 6
But i need to get 5.., sow how to make a right query?
Sure, i can do this by php, but..HOW by sql..?
Really "and" and "or" conditions does not matter, they works correctly, the problem is in counting UNION`ed query. The second query correctly counts summ (1+5), but values intersect queries. The first one gives result of just first subquery. So, i need to unique results before count...
In php, it should look like that: i get transactions id list by inner join with payments, than construct a long query in a loop, to get something like SELECT COUNT(*) FROM transactions WHERE (*what i have now* OR id=$id_1 OR id=$id_2 OR id=$id_3 etc..)
UPD: cutted
RESOLVED!=)
SELECT COUNT(*) FROM(
SELECT tr.* FROM transactions tr
WHERE tr.asset='u_{$user_id}' OR (tr.liability='g' AND tr.l_info='{$user_name}')
UNION SELECT tr.* FROM transactions tr
INNER JOIN payments pa ON tr.asset = CONCAT('p_', pa.id)
WHERE pa.user_id = '{$user_id}' AND pa.status='100')
AS total
AS total is importantly!
I gone through your question, and i think you want to fetch the maximum from the union clause, well i am not aware of the mySql, so i have solved your question in MS-SQL.
Logic:- I have used CTE, and afterwards performed UNION operation and then i have selected MAXIMUM from the two.
WITH COUNTT AS (SELECT 1 AS TEST
UNION
SELECT 5 AS TEST)
SELECT MAX(TEST) FROM COUNTT
And, in place of hardcoded "1" and "5", you can use your count query, i think it is what you are looking for. And, please mark it as an answer.