get calculation from two tables related with a third table - mysql

i have this table
table1
| id | name |
| 1 | axe |
| 2 | bow |
| 3 | car |
| 4 | dart |
and these two tables
table2 table3
| t1_id | number | | t1_id | letter |
| 1 | 5 | | 1 | a |
| 1 | 6 | | 1 | b |
| 1 | 2 | | 1 | c |
| 2 | 2 | | 2 | a |
| 2 | 2 | | 2 | c |
| 2 | 3 | | 2 | r |
| 3 | 8 | | 3 | y |
| 3 | 3 | | 3 | i |
| 3 | 1 | | 3 | a |
| 4 | 8 | | 4 | a |
| 4 | 9 | | 4 | b |
| 4 | 10 | | 4 | c |
where in it t1_id is the table1 id
what i want to do is to get all table1 records having table3 letters a b c and the avg of their numbers like this order by the letter_count DESC first then by the avg_numbers DESC
| id | name | letter_count | avg_number |
| 4 | dart | 3 | 9 |
| 1 | axe | 3 | 4.3333333333 |
| 2 | bow | 2 | 2.3333333333 |
| 3 | car | 1 | 4 |
the query i expected to work properly was http://www.sqlfiddle.com/#!9/69086b/3/0
SELECT
t1.id,
t1.name,
COUNT(t3.letter) AS letter_count,
AVG(t2.number) AS avg_number
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id
LEFT JOIN
table3 t3
ON t3.t1_id = t1.id
AND t3.letter IN ('a', 'b', 'c')
GROUP BY
t1.id
ORDER BY
letter_count DESC,
avg_number DESC
but numbers are totally different and in accurate but the order is correct
i don't want to get the letter_count and avg_number values but i just want to order by them but their values are worrying me with the query performance
i wouldn't notice this weird values because my actual query is
SELECT
t1.id,
t1.name
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id
LEFT JOIN
table3 t3
ON t3.t1_id = t1.id
AND t3.letter IN ('a', 'b', 'c')
GROUP BY
t1.id
ORDER BY
COUNT(t3.letter) DESC,
AVG(t2.number) DESC
which only gives me proper ordery
| id | name |
| 4 | dart |
| 1 | axe |
| 2 | bow |
| 3 | car |
but after checking the values i was surprsied by the letter_count do i just ignore the values and it wouldn't affect the performance in my big table?

You are aggregating over two different dimensions. This causes a Cartesian product. One way to fix this is to aggregate before joining:
SELECT t1.id, t1.name, t2.letter_count, t2.avg_number
FROM table1 t1 INNER JOIN
(SELECT t2.t1_id, AVG(t2.number) as avg_number
FROM table2 t2
GROUP BY t2.t1_id
) t2
ON t2.t1_id = t1.id LEFT JOIN
(SELECT t3.t2_id, COUNT(t3.letter) as letter_count
FROM table3 t3
WHERE t3.letter IN ('a', 'b', 'c')
GROUP BY t3.t2_id
) t3
ON t3.t1_id = t1.id
ORDER BY t3.letter_count DESC, t2.avg_number DESC;

On the third table, IN-SELECT Subquery.
SELECT
t1.id,
t1.name,
(
SELECT count(letter)
FROM t3
where t3.t1_id = t1.id
) as lettercount,
AVG(t2.number) AS avg_number
FROM
table1 t1
INNER JOIN
table2 t2
ON t2.t1_id = t1.id

Related

Select count of same entries in different tables

I got 3 tables
| ID | Name |
|:---- |:------:|
| 1 | Brie |
| 2 | Ray |
| 3 | James |
Table2
| ID | Q_id | Q_no | ans |
|:---- |:------:| -----:|----:|
| 1 | 2304. | 1 | A |
| 1 | 2304. | 2 | A |
| 1 | 2305. | 1 | C |
| 2 | 2304. | 2 | A |
| 2 | 2305. | 1 | C |
| 3 | 2304. | 1 | A |
| 3 | 2305. | 2 | D |
Table3
| Q_id | Q_no | correct_ans |
|:------:| -----:|------------:|
| 2304. | 1 | A |
| 2304. | 2 | B |
| 2305. | 1 | C |
| 2305. | 2 | D |
I need to print a table with ID, name and count of ans in table 2 where ans matches with correct answer from table 3
| ID | Name | ans_count |
|:---- |:------:| ----------:|
| 1 | Brie | 2 |
| 2 | Ray | 1 |
| 3 | James | 2 |
Select t1.ID, Name, count(t2.ans) as ans_count
from Table1 t1
join Table2 t2 on t1.ID=t2.ID
join Table3 t3 on t2.Q_id=t3.Q_id
where t2.ans=t3.correct.ans and t2.q_no=t3.q_no
group by t1.ID
order by t1.ID
Where am I doing it wrong? Sorry I am new to SQL.
You should always link the tables, with all colums that match
AND the GROUP By should contain all columns that have no aggregation function
SELECT t1.ID,t1.`Name`,COUNT(*) as correct_answers
FROM table1 t1
JOIN table2 t2 ON t1.ID = t2.ID
JOIN table3 t3 ON t2.`Q_id` = t3.`Q_id` AND t2.`Q_no` = t3.`Q_no`
WHERE t3.`correct_ans` = t2.`ans`
GROUP BY t1.ID,t1.`Name`
order by t1.ID
ID
Name
correct_answers
1
Brie
2
2
Ray
1
3
James
2
fiddle
Its still not clear what you are after, but this corrects the obvious issue with the query.
Select t1.ID, Name, count(*) as ans_count
from Table1 t1
join Table2 t2 on t1.ID=t2.ID
join Table3 t3 on t2.Q_id=t3.Q_id
where t2.ans=t3.correct.ans
group by t1.ID, t1.Name ///<---------------
order by t1.ID
I think you need to group by Id and Name

How to join tables on multiple records and multiple fields

I need a solution for a rather complex mySQL join:
Say we have 3 tables: T1, T2 and T3.
Table 1 contains the main ID and a name field.
Table1
| ID | Name |
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
Table 2 contains a column for the Table 1's ID and a value
Table2
| ID | oID | Value |
| 1 | 1 | aaa |
| 2 | 1 | bbb |
| 3 | 1 | ccc |
| 4 | 2 | ddd |
| 5 | 2 | eee |
| 6 | 3 | fff |
| 7 | 3 | ggg |
| 8 | 3 | hhh |
| 9 | 4 | iii |
So far so good, a simple JOIN will get me the Table2.Values linked to the Table1.Name
But there is also Table 3
Table3
| ID | oID | Condition | Value |
| 1 | 1 | color | red |
| 2 | 1 | brightness | 20 |
| 3 | 2 | color | green |
| 4 | 2 | brightness | 50 |
| 5 | 2 | saturation | 100 |
| 6 | 3 | color | green |
| 7 | 3 | brightness | 40 |
| 8 | 3 | saturation | 70 |
| 9 | 4 | color | purple|
What I need to do is get the T1.Name, with all the related T2.Values ONLY IF 2 (or more) conditions of T3 are OK:
For example:
All Names with matching T2.Values IF T3.Condition = 'green' AND T3.brightness = 50
As only ID 2 is green with a brightness of 50, that should give me
B ddd
B eee
I have tried several things:
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
t3.Condition = 'color'
AND
t3.Value = 'green'
AND
t3.Condition = 'brightness'
AND
t3.Value = 50
GROUP BY p.ID
Resulting in nothing of course, as no record has all four conditions
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
(t3.Condition = 'color' AND t3.Value = 'green')
AND
(t3.Condition = 'brightness'AND t3.Value = 50)
GROUP BY p.ID
Same result: nothing
SELECT t1.ID,
t1.Name,
t2.Value
FROM
Table1 t1
JOIN Table2 t2 ON t1.ID = t2.oID
JOIN Table3 t3 ON t1.ID = t3.oID
WHERE
(t3.Condition = 'color' AND t3.Value = 'green')
OR
(t3.Condition = 'brightness' AND t3.Value = 50)
GROUP BY p.ID
I get all records with brightness 50 and those with color green... but not those with both those conditions.
I also tried with conditions in the JOIN ON statement, LEFT JOINS, WHERE condition IN (..,..) and so on but without success.
The one who finds the solution gets eternal fame!
SELECT * FROM table3;
+----+-----+------------+--------+
| ID | oID | condition | value |
+----+-----+------------+--------+
| 1 | 1 | color | red |
| 2 | 1 | brightness | 20 |
| 3 | 2 | color | green |
| 4 | 2 | brightness | 50 |
| 5 | 2 | saturation | 100 |
| 6 | 3 | color | green |
| 7 | 3 | brightness | 40 |
| 8 | 3 | saturation | 70 |
| 9 | 4 | color | purple |
+----+-----+------------+--------+
9 rows in set (0.00 sec)
mysql>
SELECT x.oid
FROM table3 x
WHERE (x.condition,x.value) IN(('color','green'),('brightness','50'))
GROUP
BY x.oid
HAVING COUNT(*) = 2;
+-----+
| oid |
+-----+
| 2 |
+-----+
Note that condition is a reserved word, making it a less than ideal choice as a table/column identifier
What you are doing wrong is that you join table3 to the other 2 tables.
Instead you should join a subquery of table3 which returns the oIDs that meet your conditions:
SELECT t1.name, t2.value
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.oid
INNER JOIN (
SELECT oID FROM table3
GROUP BY oID
HAVING SUM(`condition` = 'color' AND value = 'green') > 0
AND SUM(`condition` = 'brightness' AND value = '50') > 0
) t3 ON t1.id = t3.oID
See the demo.
Results:
| name | value |
| ---- | ----- |
| B | ddd |
| B | eee |

SQL query to get minimal sum value and associated column from table

I have following tables:
t1 - Items
| Id | Item |
| 1 | I1 |
| 2 | I2 |
t2 - Item_elements
| Id | Item_id | Element | Our_quantity | Client_quantity |
| 1 | 1 | E11 | 100 | 0 |
| 2 | 1 | E12 | 20 | 300 |
| 3 | 2 | E21 | 300 | 100 |
| 4 | 2 | E22 | 5 | 300 |
t3 - Element_operations
| Id | Element_id | Operations_number |
| 1 | 1 | 100 |
| 2 | 1 | 50 |
| 3 | 2 | 50 |
| 4 | 2 | 50 |
| 5 | 3 | 50 |
| 6 | 3 | 50 |
| 7 | 3 | 50 |
| 8 | 3 | 50 |
| 9 | 4 | 10 |
I need SQL query which return table witch items (t1) AND element row associated with item table which has minimum operations number
Desired Output:
Table should look like
| Id|Item| Id_el |Our_quantity| Client_quantity | Count Operations_number|
| 1| I1 | 2 | 20 | 300 | 100 |
| 2| I2 | 4 | 5 | 300 | 10 |
result
I tried that query
SELECT t2.Id,Item_id,Our_Quantity,Client_Quantity,SUM(Operations_number)
FROM t2 LEFT JOIN t3 ON t2.Id=t3.Id_el GROUP BY t2.Id)
Tried Result:
| Id | Item_id | Our_quantity | Client_quantity |SUM(Operations_number)
| 1 | 1 | 100 | 0 | 150
| 2 | 1 | 20 | 300 | 100
| 3 | 2 | 300 | 100 | 200
| 4 | 2 | 5 | 300 | 10
What should I do next?
Now I have 2 table:
| Element Id | Item_id |Sum operations_number for element |
| 1 | 1 | 150 |
| 2 | 1 | 100 |
| 3 | 2 | 200 |
| 4 | 2 | 10 |
| Item Id | Item |
| 1 | I1 |
| 2 | I2 |
How can I join them to get this table?
Item Element who has minimal sum operations number.
| Item Id | Item | Element_Id | Sum operations_number for element |
| 1 | I1 | 2 | 100 |
| 2 | I2 | 4 | 10 |
You could get desired output using this..
SELECT
t.*,
MIN(t.opsum) AS `Count Operations_number`
FROM
(SELECT
a.*,
b.Id AS `Id_el`,
b.`Our_quantity`,
b.`Client_quantity`,
SUM(c.`Operations_number`) AS opsum
FROM
`t1` AS a
LEFT JOIN `t2` AS b
ON a.`Id` = b.`Item_id`
LEFT JOIN `t3` AS c
ON b.`Id` = c.`Element_id`
GROUP BY a.Id,
b.Id
ORDER BY a.`Id` ASC,
opsum ASC) AS t
GROUP BY t.Id ;
FIDDLE HERE
Maybe if you use the MIN() method in the desired column, as example:
SQL
SELECT t2.Id,Item_id, MIN(Our_Quantity), Client_Quantity, SUM(Operations_number)
FROM t2 LEFT JOIN t3
ON t2.Id=t3.Id_el
GROUP BY t2.Id
You may try this..
For mysql 8+
with cte as (
SELECT t1.id as Item_id, t1.item, t2.id as Ele_Id, t2.Element , t2.Our_quantity , t2.Client_quantity , t3.Operations_number
from Items as t1
inner join Item_elements as t2 on t1.id=t2.Item_id
inner join Item_elements as t3 on t2.id = t3.Element_id
)
, ct as (
select row_number() over ( partition by t1.id order by t2.Our_quantity ) as Slno, * from cte
)
select c.item_id, c.item, c.Ele_id, c.Element, c.our_quantity, c.client_quantity, t.Count_Operations_number
from ct as c
inner join
(
select distinct Element_id , sum(Operation_number) as Count_Operations_number
from Element_operations group by Element_id
) as t
on c.Ele_id=t.Element_id
where c.slno=1
Try the below query, hope this is what you are asking
select t1.Id as 'Id',t1.Item as 'Item',
t.Id as 'Id_el', t.Our_quantity as 'Our_quantity',t.Client_quantity as 'Client_quantity',
sum(t3.Operations_number) as 'Count Operations_number'
from t1 join (select *
from t2 where (Item_id,Our_quantity) in
( select Item_id, min(Our_quantity) from t2 group by Item_id)) t on t1.Id=t.Item_id
join t3 on t.Id=t3.Element_id
group by t1.Id;
Do you want it in the minimum order of SUM(Operations_number)?
Try this
I have updated the answer so this will get the first table also.
SELECT t1.id,
t1.item,
id_el,
our_quantity,
client_quantity,
sum
FROM t1
JOIN (SELECT t2.id AS Id_el,
t2.item_id,
t2.our_quantity AS our_quantity,
t2.client_quantity AS client_quantity,
sum
FROM t2
JOIN (SELECT t3.element_id,
Sum(operations_number) AS sum
FROM t3
GROUP BY element_id) AS b
ON t2.id = b.element_id) AS c
ON t1.id = c.item_id
ORDER BY sum ASC
Output would be:

MySQL select all rows having latest value per id

I have table that looks like this:
| id | user | data |
--------------------
| 1 | 11 | aaa1 |
| 2 | 11 | aaa2 |
| 3 | 11 | aaa3 |
| 4 | 22 | aaa4 |
| 5 | 33 | aaa5 |
| 6 | 33 | aaa6 |
| 7 | 44 | aaa7 |
I want to select all rows, with all data, and I want to add data with max id per user, that should look like this:
| id | user | data | f_id | f_data |
------------------------------------
| 1 | 11 | aaa1 | 3 | aaa3 |
| 2 | 11 | aaa2 | 3 | aaa3 |
| 3 | 11 | aaa3 | 3 | aaa3 |
| 4 | 22 | aaa4 | 4 | aaa4 |
| 5 | 33 | aaa5 | 6 | aaa6 |
| 6 | 33 | aaa6 | 6 | aaa6 |
| 7 | 44 | aaa7 | 7 | aaa7 |
this is my attempt of query:
SELECT t1.*, t2.id AS f_id, t2.data AS f_data
FROM table1 t1
LEFT JOIN table1 t2
ON t1.user=(SELECT MAX(t2.id)
FROM table1 t2
WHERE t2.user = t1.user )
Update:
All the answers are correct, but when I run the Query on table with 80K+ rows, MySQL needs a lot of time to Execute the query. For my project I will add ajax so user could click on it and php would execute query for one row per click.
SQL Fiddle Demo
SELECT t1.*,
t2.id AS f_id,
t2.data AS f_data
FROM table1 t1
JOIN table1 t2
ON t1.user = t2.user
AND t2.id = (SELECT MAX(t2.id)
FROM table1 t3
WHERE t3.user = t1.user)
You can approach this using your logic. Just a few things need to be fixed up:
SELECT t1.*, t2max.id AS f_id, t2max.data AS f_data
FROM table1 t1 JOIN
table1 t2max
ON t1.user = t2max.user AND
t2max.id = (SELECT MAX(t2.id)
FROM table1 t2
WHERE t2.user = t1.user
);
The changes are:
You need a condition between the two tables in the FROM clause, for the user.
The subquery needs to join on the id not on the user.
This will do it.
SELECT t1.*, t3.id AS f_id, concat(SUBSTRING(t1.data, 1, CHAR_LENGTH(t1.data)-1),t3.id) AS f_data
FROM table1 t1
INNER JOIN (select max(t2.id) as id, t2.user as userid from table1 t2 group by t2.user) t3
on t3.userid = t1.user
Sqlfiddle : http://sqlfiddle.com/#!9/a24413/7

MySQL inner join where table2 child_id is like table2 id

I have two tables and as the title says I need something like a double inner join no. I have no idea if that works but I believe there should be an easy way.
What I've got is this statement which works fine:
SELECT
t1.id img_id, t1.nav_id img_nav_id, t1.name img_name, t1.img_title img_title, t1.img_text img_text,
t2.id nav_id,t2.parent_id nav_parent_id, t2.name nav_name, t2.directlink nav_directlink
FROM images t1
INNER JOIN navigation t2
ON t2.id=t1.nav_id
ORDER BY RAND() LIMIT 0,101
now t2 (navigation table) looks like this
+----+-----------+------------------+------------------+------+
| id | parent_id | name | directlink | rang |
+----+-----------+------------------+------------------+------+
| 1 | 0 | Home | home | 0 |
| 3 | 0 | Architektur | architektur | 1 |
| 7 | 0 | Design | design | 2 |
| 8 | 0 | Contact | contact | 3 |
| 11 | 3 | Surfabricaziun 5 | surfabricaziun_5 | 0 |
| 12 | 7 | Fluor | fluor | 1 |
| 13 | 7 | Maisa | maisa | 2 |
| 14 | 3 | Fuldera | fuldera | 3 |
and t1 (images table) looks like this
+-----+--------+------+----------------------+-----------+----------+
| id | nav_id | rang | name | img_title | img_text |
+-----+--------+------+----------------------+-----------+----------+
| 700 | 11 | 80 | Siedlg_aussen_26.jpg | | |
the output I get from the sql statement is:
+--------+------------+-------------+-----------+----------+--------+---------------+----------+------------------+
| img_id | img_nav_id | img_name | img_title | img_text | nav_id | nav_parent_id | nav_name | nav_directlink |
+--------+------------+-------------+-----------+----------+--------+---------------+----------+------------------+
| 625 | 11 | 07.jpg | 11 | | 11 | 3 | Surfabri | surfabricaziun_5 |
| 744 | 20 | 85.jpg | | | 20 | 7 | Test | test |
now What I want or need is: I need to get the parent nav name. So I would like one more field called nav_parent_name where t2.parent_id = t2.id and for this I've tried
SELECT
t1.id img_id, t1.nav_id img_nav_id, t1.name img_name, t1.img_title img_title, t1.img_text img_text,
t2.id nav_id,t2.parent_id nav_parent_id, t2.name nav_name, t2.directlink nav_directlink,
t2.name nav_parent_name
FROM images t1
INNER JOIN navigation t2
INNER JOIN navigation t2
ON t2.parent_id = t2.id AS nav_parent_name
ON t2.id=t1.nav_id
ORDER BY RAND() LIMIT 0,101
which isn't working. Problem: I don't know anything about join is it possible to get the result I want or do I have to write a new Sql statement which would be easy but I would love to have only one statement working for all data I need.
Thanks in advance to everyone reading for any suggetions and advices.
you have some aliasing problems and need to keep your existing join while adding a new on
SELECT
t1.id img_id, t1.nav_id img_nav_id, t1.name img_name, t1.img_title img_title, t1.img_text img_text,
t2.id nav_id,t2.parent_id nav_parent_id, t2.name nav_name, t2.directlink nav_directlink,
t3.name nav_parent_name
FROM images t1
INNER JOIN navigation t2
ON t2.id=t1.nav_id
INNER JOIN navigation t3
ON t2.parent_id = t3.id
ORDER BY RAND() LIMIT 0,101