how to use sum function with join in MySQL - mysql

select sum(bales) as bales
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where id=1
my result is showing wrong output

Your query 1)should not even syntax given id is ambiguous in the where clause 2) the joined table is pointless since you don't use anything from it in the select 3) From the little information you have provided in the question there is a 1 to many relationship between receive_bardana and receive_wheat which means that the sum is over all the joined rows for example
create table receive_bardana(id int,bales int);
create table receive_wheat(id int);
insert into receive_bardana values (1,10),(2,20);
insert into receive_wheat values(1),(1),(2),(2),(2);
select *
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where receive_bardana.id;
Result
+------+-------+------+
| id | bales | id |
+------+-------+------+
| 1 | 10 | 1 |
| 1 | 10 | 1 |
| 2 | 20 | 2 |
| 2 | 20 | 2 |
| 2 | 20 | 2 |
+------+-------+------+
5 rows in set (0.00 sec)
and with your (fixed) query
select sum(bales) as bales
from receive_bardana
join receive_wheat
on receive_bardana.id = receive_wheat.id
where receive_bardana.id;
the sum correctly returns
+-------+
| bales |
+-------+
| 80 |
+-------+
1 row in set (0.00 sec)
Which explains fully the 'problem' you are having.
If you want a answer to what you are trying to do I suggest you raise a new question describing what you are trying to do rather than just saying this lump of code does not do as I expect (in fact it does what I expect and is not 'wrong')

Related

Get records from one table and a corresponding table

I have two tables:
orders
poid | user | pid | payment_id
1 | 1 | 1 | abc123
2 | 2 | 2 | def345
orders_addon
poaid | user | poid | pid
1 | 1 | 1 | 3
2 | 1 | 1 | 5
One represents orders, the second one represent addons a user can add to his order.
There is always a row in orders and it can occur that there is no matching orders_addon for an order.
I'm looking for a query that returns matching rows from orders and orders_addon if there are matching ones.
SELECT user,pid FROM ... WHERE payment_id = 'abc123'
Should return
user | pid
1 | 1
1 | 3
1 | 5
And the same query should only return results from the orders table if there is no matching record in the orders_addon table.
SELECT user,pid FROM ... WHERE payment_id = 'def345'
user | pid
2 | 2
I reckon this could be done using UNION but then I wouldn't be able to match the tables and it would become a problem since the orders_addon table doesn't have a payment_id
Use LEFT JOIN WITH IF STATMENT
mysql> ( SELECT u.user,IFNULL(ua.pid ,u.pid) as pid
FROM orders u
inner JOIN orders_addon ua on ua.poid=u.poid
WHERE u.payment_id = 'abc123'
)
union all
( SELECT u.user,u.pid
from orders u
where u.payment_id = 'def345'
);
+------+------+
| user | pid |
+------+------+
| 1 | 3 |
| 1 | 5 |
| 2 | 2 |
+------+------+
3 rows in set (0.00 sec)
mysql> ( SELECT u.user,IFNULL(ua.pid ,u.pid) as pid
FROM orders u
inner JOIN orders_addon ua on ua.poid=u.poid
WHERE u.payment_id = 'def345'
)
union all
( SELECT u.user,u.pid
from orders u
where u.payment_id = 'def345'
);
+------+------+
| user | pid |
+------+------+
| 2 | 2 |
+------+------+
1 row in set (0.00 sec)

MySQL Combining Tables Specific Order

This seems to be a convoluted problem, but I'll try my best to articulate the idea and illustrate a scenario. Essentially I have two tables that need to be combined and returned as the result set for a single query. One table needs to be merged into the other in a specific order.
Say table one is called Articles and table two is called Features. Both tables have an ID field with unique numbers. Articles has a date field which will be used to initially sort its records in descending order. The Features table has a Delta field which be used initially to sort its records. Some of the records in the Features table are placeholders and are not meant to be included in the final set. Their only purpose is to affect the sort order. Each record has a unique value in the Delta field, from 1 - X which will be used to sort these records. Another field called Skip has a value of 1 if it should be eliminated when merging the two tables together. Again, the only purpose to the skipped records is to take up space during the initial sort on the Features table. Even though they are unnecessary, they exist and can't be deleted.
The tricky part is that when the results from both tables are merged, any non-skipped records from the Features table need to be inserted into the results from the Articles table in the exact order they appears in the Features table.
So lets say I have 6 records in the Features table, A - F and the order field ranges from 1 - 6. Records A,B,D,E all have a value of 1 in the Skip field. That means I'm only interested in records C and F both of which need to be inserted into the final record set in positions 3 and 6 respectively.
The records may look something like this for the Articles table:
+----+------------+
| id | date |
+----+------------+
| 1 | 9999999999 |
+----+------------+
| 2 | 9999999998 |
+----+------------+
| 3 | 9999999997 |
+----+------------+
| 4 | 9999999996 |
+----+------------+
| 5 | 9999999995 |
+----+------------+
| 6 | 9999999994 |
+----+------------+
| 7 | 9999999993 |
+----+------------+
| 8 | 9999999992 |
+----+------------+
| 9 | 9999999991 |
+----+------------+
| 10 | 9999999990 |
+----+------------+
The Features table may look something like this:
+----+------+-------+------+
| id | name | delta | skip |
+----+------+-------+------+
| 11 | A | 1 | 1 |
+----+------+-------+------+
| 12 | B | 2 | 1 |
+----+------+-------+------+
| 13 | C | 3 | 0 |
+----+------+-------+------+
| 14 | D | 4 | 1 |
+----+------+-------+------+
| 15 | E | 5 | 1 |
+----+------+-------+------+
| 16 | F | 6 | 0 |
+----+------+-------+------+
The results would look something like this (not including any additional fields that might be needed to achieve my goal):
+----+
| id |
+----+
| 1 |
+----+
| 2 |
+----+
| 13 | (record from the Features table in the third position)
+----+
| 3 |
+----+
| 4 |
+----+
| 16 | (record from the Features table in the sixth position)
+----+
| 5 |
+----+
| 6 |
+----+
| 7 |
+----+
| 8 |
+----+
| 9 |
+----+
| 10 |
+----+
Hope my explanation makes sense. Any ideas?
Thanks,
Howie
I assume that there is a mistake in your example - record id=16 is sixth row in Features table, so should be after id=5 in results, not before.
Try the blelow query. Here is SQLFiddle.
select id from (
select `date`, null delta, id
from Articles
union all
select a.`date`, f.delta, f.id
from (
select (#x:=#x+1) rn, a.*
from Articles a, (select #x:=0) z
order by a.`date` desc
) a
join (
select (#y:=#y+1) rn, f.id, f.delta, f.skip
from Features f, (select #y:=0) z
order by f.delta
) f
on a.rn = f.rn
where f.skip <> 1
order by `date` desc, isnull( delta ), delta
) merge
Looks like this example in SQL Fiddle did it for me.
SELECT id, sort_order FROM (
SELECT `date`, NULL delta, id, (#a_count:=#a_count+1) sort_order
FROM Articles a_main, (SELECT #a_count:=-1) z
UNION ALL
SELECT a.`date`, f.delta, f.id, f.weighted_rn
FROM (
SELECT (#x:=#x+1) rn, a.*
FROM Articles a, (SELECT #x:=-1) z
ORDER BY a.`date` DESC
) a
JOIN (
SELECT (#y:=#y+1) rn, TRUNCATE((f.delta - #y - (1/#y)),2) AS weighted_rn, f.id, f.delta, f.skip
FROM Features f, (SELECT #y:=-1) z
WHERE f.skip <> 1
ORDER BY f.delta
) f
ON a.rn = f.rn
ORDER BY sort_order
) merge
Thanks to Kordirko for the framework.

mysql multiple many to many joins

I have events, events_styles, events_formats tables (an event can have many styles and many formats)
I am trying to filter events which have a events_styles join and events_formats join. So the query should select all events which are a particular style AND a particular format - my attempts so far:
SELECT * FROM events
JOIN events_styles ON events.id = events_styles.event_id
JOIN events_formats ON events.id = events_formats.format_id
WHERE events_styles.style_id = 3 AND events_formats.format_id = 1;
Empty set (0.00 sec)
SELECT * FROM events_styles
WHERE events_styles.style_id = 3
+----------+----------+
| event_id | style_id |
+----------+----------+
| 3 | 3 |
| 2 | 3 |
| 4 | 3 |
+----------+----------+
3 rows in set (0.00 sec)
SELECT * FROM events_formats
WHERE events_formats.format_id = 1
+----------+-----------+
| event_id | format_id |
+----------+-----------+
| 1 | 1 |
| 3 | 1 |
| 4 | 1 |
+----------+-----------+
3 rows in set (0.00 sec)
So the first query should return the event with id 4?
Im sure I need to combine 2nd and 3rd query into a subquery but Im unsure of the syntax - thanks
You are very close!
Your query should be:
SELECT * FROM events
JOIN events_styles ON events.id = events_styles.event_id
JOIN events_formats ON events.id = events_formats.event_id
WHERE events_styles.style_id = 3
AND events_formats.format_id = 1;
By joining on format_id and not event_id you are only going to get event_id = 1 which has no correspoding style id of 3. That's where you went wrong :-)

Complex Join - involving date ranges and sum

I have two tables that I need to join... I want to join table1 and table2 on 'id' - however in table two id is not unique. I only want one value returned for table two, and this value represents the sum of a column called 'total_sold' - within a specified date range (say one month), however I want more than one date range at the same time...
SELECT ta.id, sum(tb.total_sold) as total_sold_this_week, sum(tc.total_sold) as total_sold_this_month
FROM table_a as ta
LEFT JOIN table_b as tb ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 WEEK) AND NOW()
LEFT JOIN table_b as tc ON ta.id=tc.id AND tc.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW()
GROUP BY ta.id
this works but does not SUM the rows - only returning one row for each id... how do I get the sum from table b instead of only one row???
Please criticise if format of question could use more work - I can rewrite and provide sample data if required - this is a trivialised version of a much larger problem.
-Thanks
Using Subqueries
One way to solve this would be to use subqueries. LEFT JOIN creates a new "result" for each match in the right table, so using two LEFT JOINs is creating more ROWS than you want. You could just sub select the value you want, but this can be slow:
SELECT ta.id,
(SELECT SUM(total_sold) as total_sold
FROM table_b
WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 WEEK) AND NOW()
AND id=ta.id) as total_sold_this_week,
(SELECT SUM(total_sold) as total_sold
FROM table_b
WHERE date_sold BETWEEN ADDDATE(NOW(), INTERVAL -1 MONTH) AND NOW()
AND id = ta.id) as total_sold_this_month
FROM table_a ta;
Result:
+----+----------------------+-----------------------+
| id | total_sold_this_week | total_sold_this_month |
+----+----------------------+-----------------------+
| 1 | 3 | 7 |
| 2 | 4 | 4 |
| 3 | NULL | NULL |
+----+----------------------+-----------------------+
3 rows in set (0.04 sec)
Using SUM(CASE ...)
This method doesn't use subqueries (and will likely be faster on larger data sets). We want to join the table_a and table_b together once, using our "biggest" date range, and then use a SUM() based on a CASE to calculate the "smaller range".
SELECT ta.*,
SUM(total_sold) as total_sold_last_month,
SUM(CASE
WHEN date_sold BETWEEN NOW() - INTERVAL 1 WEEK AND NOW()
THEN total_sold
ELSE 0
END) as total_sold_last_week
FROM table_a AS ta
LEFT JOIN table_b AS tb
ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -1 MONTH) AND NOW()
GROUP BY ta.id;
This returns nearly the same resultset as the subquery example:
+----+-----------------------+----------------------+
| id | total_sold_last_month | total_sold_last_week |
+----+-----------------------+----------------------+
| 1 | 7 | 3 |
| 2 | 4 | 4 |
| 3 | NULL | 0 |
+----+-----------------------+----------------------+
3 rows in set (0.00 sec)
The only difference is the 0 instead of NULL. You could summarize as many date ranges as you'd like using this method, but its still probably best to limit the rows returned to the largest range in the ON clause.
Just to show how it works: removing the GROUP BY and SUM() calls, and adding date_sold to the SELECT returns this:
+----+------------+-----------------------+----------------------+
| id | date_sold | total_sold_last_month | total_sold_last_week |
+----+------------+-----------------------+----------------------+
| 1 | 2010-04-30 | 2 | 2 |
| 1 | 2010-04-24 | 2 | 0 |
| 1 | 2010-04-24 | 2 | 0 |
| 1 | 2010-05-03 | 1 | 1 |
| 2 | 2010-05-03 | 4 | 4 |
| 3 | NULL | NULL | 0 |
+----+------------+-----------------------+----------------------+
6 rows in set (0.00 sec)
Now when you GROUP BY id, and SUM() the two total_sold columns you have your results!
Old Advice
Before you brought the two different date ranges into the mix, you could use GROUP BY to group using the table id on table1, and the SUM() aggregate function to add up the rows returned.
SELECT ta.id, SUM(tb.total_sold) as total_sold_this_week
FROM table_a as ta
LEFT JOIN table_b as tb
ON ta.id=tb.id AND tb.date_sold BETWEEN ADDDATE(NOW(),INTERVAL -3 WEEK) AND NOW()
GROUP BY ta.id
+----+----------------------+
| id | total_sold_this_week |
+----+----------------------+
| 1 | 7 |
| 2 | 4 |
| 3 | NULL |
+----+----------------------+
3 rows in set (0.00 sec)
The test data
NOW() is 2010-05-03
mysql> select * from table_a; select * from table_b;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec)
+----+------------+------------+
| id | date_sold | total_sold |
+----+------------+------------+
| 1 | 2010-04-24 | 2 |
| 1 | 2010-04-24 | 2 |
| 1 | 2010-04-30 | 2 |
| 1 | 2010-05-03 | 1 |
| 2 | 2010-05-03 | 4 |
+----+------------+------------+
5 rows in set (0.00 sec)

Help with MySQL query to select only records with specific value in column

I have following table:
+--------+-----------+--------+
| ID | Title | Stat |
+--------+-----------+--------+
| 1 | title a | 1 |
+--------+-----------+--------+
| 1 | title b | 0 |
+--------+-----------+--------+
| 2 | title c | 1 |
+--------+-----------+--------+
| 2 | title d | 1 |
+--------+-----------+--------+
| 3 | title e | 0 |
+--------+-----------+--------+
| 3 | title f | 1 |
+--------+-----------+--------+
| 3 | title g | 1 |
+--------+-----------+--------+
I need to construct a query, where as a result only those records would be included where ALL values in column Stat meet certain condition.
For above table, how to formulate a query that would basically mean: show me records for only those ID's that have all the values in column Stat = 1 and would as a result produce following table?
+--------+-----------+--------+
| 2 | title c | 1 |
+--------+-----------+--------+
| 2 | title d | 1 |
+--------+-----------+--------+
Thanks.
Something like this?
SELECT
*
FROM table t
LEFT JOIN (
SELECT
MIN(Stat) AS Stat,
ID
FROM table
GROUP BY ID
) minstat ON t.ID = minstat.ID
WHERE minstat.Stat = 1;
Basically, the subquery computes the minimum stat per ID, whereas the outer query only selects the rows with the minimal stat equal to 1. If all rows need to satisfy a certain condition, insert a boolean expression in stead of Stat in the minimum.
Not sure if this is possible without a subquery.
This also works,
SELECT * FROM tablename t1
WHERE (
SELECT COUNT(*) FROM tablename t2
WHERE t1.ID = t2.ID AND t2.STAT = 0
) = 0
this query checks, is where a STAT with value 0 in records with same ID.
How about this:
select * from t where not (id in (select id from t where stat=0));
All 3 suggested solutions above work well, difference is only in the speed of execution. On a table where 3 records are found among approximately 7000 results are:
Martijn's: Showing rows 0 - 2 (3 total, Query took 0.0069 sec)
Alex's: Showing rows 0 - 2 (3 total, Query took 0.1162 sec)
Pavelasc's: Showing rows 0 - 2 (3 total, Query took 0.6160 sec)