select using sub select - php

I have a table with stats where each row includes player_id, game_id, and team_id. I am trying to create a query to return result where distinct games are returned where two or more players played together in a game on same team. I trying to create a select statement like this but obviously this is wrong.
SELECT * FROM (
SELECT * FROM stats AS t1 WHERE player_id IN (array)
) AS t2
WHERE t1.game_id=t2.game_id AND t1.team_id=t2.team_id

If your table has a id you can use this sql with INNER JOIN
SELECT t1.*,t2.*
FROM stats t1
INNER JOIN stats t2
ON t1.game_id=t2.game_id
AND t1.team_id=t2.team_id
AND t2.id <> t1.id
WHERE t1. player_id IN (array);
The you can modify the SELECT to SELECT DISTINCT t1.game_id
here my result of your table:
MariaDB [Bernd]> SELECT * FROM softball_stats;
+-------------+-----------+---------+---------------+
| sb_stats_id | player_id | game_id | sb_stats_team |
+-------------+-----------+---------+---------------+
| 1 | 100 | 1 | 1000 |
| 2 | 100 | 2 | 1000 |
| 3 | 100 | 3 | 1010 |
| 4 | 101 | 2 | 1000 |
| 5 | 102 | 3 | 1010 |
| 6 | 103 | 1 | 1000 |
+-------------+-----------+---------+---------------+
6 rows in set (0.01 sec)
MariaDB [Bernd]>
MariaDB [Bernd]> SELECT t1.*,t2.*
-> FROM softball_stats t1
-> INNER JOIN softball_stats t2
-> ON t1.game_id=t2.game_id
-> AND t1.sb_stats_team=t2.sb_stats_team
-> AND t2.sb_stats_id <> t1.sb_stats_id
-> WHERE t1. player_id IN (100,101);
+-------------+-----------+---------+---------------+-------------+-----------+---------+---------------+
| sb_stats_id | player_id | game_id | sb_stats_team | sb_stats_id | player_id | game_id | sb_stats_team |
+-------------+-----------+---------+---------------+-------------+-----------+---------+---------------+
| 4 | 101 | 2 | 1000 | 2 | 100 | 2 | 1000 |
| 2 | 100 | 2 | 1000 | 4 | 101 | 2 | 1000 |
| 3 | 100 | 3 | 1010 | 5 | 102 | 3 | 1010 |
| 1 | 100 | 1 | 1000 | 6 | 103 | 1 | 1000 |
+-------------+-----------+---------+---------------+-------------+-----------+---------+---------------+
4 rows in set (0.01 sec)
MariaDB [Bernd]>

Related

copy (with update) certain content from the same table

I wanted to UPDATE the value of my below table (row & col_md) :
Current Data
| id | id_cat | row | col_md |
| --- | ------ | ---- | ------ |
| 1 | 1 | 1 | 4 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 3 | 5 |
| 4 | 2 | 1 | 3 |
| 5 | 2 | 2 | 4 |
| 6 | 2 | 2 | 4 |
| 7 | 3 | 1 | 12 |
| 8 | 3 | 1 | 12 |
| 9 | 3 | 2 | 3 |
That may look something like the below table. (I want to have the same content of rows that id_cat=1 have, in rows with id_cat=2 & 3).
Required Data:
| id | id_cat | row | col_md |
| --- | ------ | ---- | ------ |
| 1 | 1 | 1 | 4 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 3 | 5 |
| 4 | 2 | 1 | 4 |
| 5 | 2 | 2 | 5 |
| 6 | 2 | 3 | 5 |
| 7 | 3 | 1 | 4 |
| 8 | 3 | 2 | 5 |
| 9 | 3 | 3 | 5 |
id_cat 2 and 3 should have the same "row" and "col_md" values as in id_cat=1.
I've tried with this post first answer like this:
UPDATE `myTable` AS t1 JOIN `myTable` AS t2 ON t2.id_cat=1
SET t1.row = t2.row, t1.col_md = t2.col_md
WHERE t1.id_cat = 2 or t1.id_cat=3;
but that results on all "row" column values equal to 1.
What I'm doing wrong and what's the way to do this right?
EDIT:
The tables above are just examples to make this ask easier to understand, but the real table is bigger (4k rows) and:
"row" column with id_cat=1 can have any number and not a sequence as in the example.
"col_md" columns can have any number too.
That's why the update must set a copy of the id_cat=1 "row" and "col_md" values in the id_cat!=1 "row" and "col_md" values.
If this can't be done with just MySQL, a php script will be nice too.
In the example query you gave, you are updating t1.row with t2.row. As you are joining on the id_cat, this will result in multiple rows selected to update a single row, so the outcome just takes the first row.
What you actually want, is to make the 1-to-1 relation in the update, so what needs to be changed in your query is to add the row matching in the join and remove the assignment in the SET, like this:
UPDATE `myTable` AS t1 JOIN `myTable` AS t2 ON t2.id_cat=1 AND t1.row = t2.row
SET t1.col_md = t2.col_md
WHERE t1.id_cat = 2 or t1.id_cat=3;
Which then gives the output of:
MariaDB [testart]> select * from myTable;
+------+--------+------+--------+
| id | id_cat | row | col_md |
+------+--------+------+--------+
| 1 | 1 | 1 | 4 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 3 | 5 |
| 4 | 2 | 1 | 4 |
| 5 | 2 | 2 | 5 |
| 6 | 2 | 3 | 5 |
| 7 | 3 | 1 | 4 |
| 8 | 3 | 1 | 4 |
| 9 | 3 | 2 | 5 |
+------+--------+------+--------+
9 rows in set (0.00 sec)
Currently able to achieve the SQL query for your desired result.
SELECT t2.id_cat, t1.row, t1.col_md
FROM (SELECT row, col_md from mytable WHERE id_cat=1) as t1 , mytable as t2
GROUP BY t2.id_cat, t1.row, t1.col_md
The above will return the following..
I suggest to use INSERT statement along with the above query to put the record into a new table and drop the old one.
Cheers!
EDITED...
Instead of Updating table, alternate approach could be to Insert the required record into a new table.
This can be achieved with following four steps
Create a tmp table with same fileds (id Auto_Increment, id_cat, row, col_md)
Insert to tmp table with this statement...
INSERT INTO tmp(id_cat, row, col_md)
SELECT t2.id_cat, t1.row, t1.col_md
FROM (SELECT row, col_md from mytable WHERE id_cat=1) as t1 , mytable as t2
GROUP BY t2.id_cat, t1.row, t1.col_md
Remove/Rename 'myTable'.
Rename 'tmp' table to 'myTable'.
Hope this will serve the purpose...
Cheers!
it's not enough to tell which group you want the data from, you need to match id to id.
in your case t2.id 4 and 7 to t1.id 1, t2.id 5 and 8 to t1.id 2, and t2.id 6 and 9 to t1.id 3.
SELECT #d := COUNT(*) FROM myTable WHERE id_cat = 1;
UPDATE `myTable` AS t1
JOIN `myTable` AS t2 ON t2.id_cat=1 AND
t2.id = IFNULL(NULLIF(t1.id MOD #d, 0), #d)
SET t1.row = t2.row, t1.col_md = t2.col_md
WHERE t1.id_cat = 2 or t1.id_cat=3;
#d holds the number of lines where id_cat = 1
we divide t1.id by #d and match the remainder (MOD) to t2.id.
when t1.id is multiple of #d the remainder is 0 and we have to match it to #d
so we make 0 into NULL and NULL into #d
In my understanding, the difficult part about this question is to relate each record to update (ie each record with id_cat IN (2, 3)) to the relevant original record (record with id_cat = 1).
Based on your sample data, I understand that you expect series of records for each id_cat (I can see three groups of three records, sorted by increasing id), so I would assume that you want to relate each record to the original that has the same sequence in the group of record where id_cat = 1.
Assuming MySQL 8.0, a typical approach to assign a number to a record within a group is ROW_NUMBER(). Consider this simple query:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY id_cat ORDER BY id) rn
FROM t
Yields:
| id | id_cat | rw | col_md | rn |
| --- | ------ | --- | ------ | --- |
| 1 | 1 | 1 | 4 | 1 |
| 2 | 1 | 2 | 5 | 2 |
| 3 | 1 | 3 | 5 | 3 |
| 4 | 2 | 1 | 3 | 1 |
| 5 | 2 | 2 | 4 | 2 |
| 6 | 2 | 2 | 4 | 3 |
| 7 | 3 | 1 | 12 | 1 |
| 8 | 3 | 1 | 12 | 2 |
| 9 | 3 | 2 | 3 | 3 |
Now with this set-up in mind, we can turn this query to a Common Table Expression (available also starting MySQL 8.0), and JOIN it as need with the original table to do the UPDATE:
WITH cte AS (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY id_cat ORDER BY id) rn
FROM t
)
UPDATE t t0
INNER JOIN cte t1 ON t1.id = t0.id
INNER JOIN cte t2 ON t2.id_cat = 1 AND t2.rn = t1.rn
SET t0.rw = t2.rw, t0.col_md = t2.col_md
WHERE t0.id_cat IN (2, 3)
Details:
t0 is the original table, where records having id_cat IN (2, 3) need to be updated
t1 is the corresponding record in the CTE (to which a row number was assigned)
t2 is the record in the CTE that has id_cat = 1 and the same row number as the record being updated
Demo on DB Fiddle:
| id | id_cat | rw | col_md |
| --- | ------ | --- | ------ |
| 1 | 1 | 1 | 4 |
| 2 | 1 | 2 | 5 |
| 3 | 1 | 3 | 5 |
| 4 | 2 | 1 | 4 |
| 5 | 2 | 2 | 5 |
| 6 | 2 | 3 | 5 |
| 7 | 3 | 1 | 4 |
| 8 | 3 | 2 | 5 |
| 9 | 3 | 3 | 5 |

sum the quantity from 3rd relational table

I have following tables, i want to fetch the purchase_order and his order_quantity and sum of received quantity for each purchase_order. i know how to sum the quantity from single table but from multiple tables, it is confusing me a lot...
mysql> select * from purchase_order;
+-------------------+-------------------------+-------+---------------------+
| purchase_order_id | purchase_order | cost | created_on |
+-------------------+-------------------------+-------+---------------------+
| 1 | Dell Computer 000001256 | 10000 | 2015-02-19 22:14:52 |
| 2 | HP Computer 000001256 | 50000 | 2015-02-19 22:14:52 |
+-------------------+-------------------------+-------+---------------------+
2 rows in set (0.00 sec)
mysql> select * from purchase_order_detail;
+--------------------------+-------------------+---------+------------------+
| purchase_order_detail_id | purchase_order_id | item_id | ordered_quantity |
+--------------------------+-------------------+---------+------------------+
| 1 | 1 | 279 | 100 |
| 2 | 1 | 286 | 100 |
| 3 | 2 | 279 | 200 |
| 4 | 2 | 286 | 300 |
+--------------------------+-------------------+---------+------------------+
4 rows in set (0.00 sec)
mysql> select * from delivery_order;
+-------------------+--------------------------+-------------------+---------------------+
| delivery_order_id | purchase_order_detail_id | recieved_quantity | recieved_on |
+-------------------+--------------------------+-------------------+---------------------+
| 1 | 1 | 50 | 2015-02-19 22:22:51 |
| 2 | 2 | 50 | 2015-02-19 22:24:59 |
| 3 | 1 | 50 | 2015-02-19 22:34:14 |
| 4 | 3 | 70 | 2015-02-20 11:11:31 |
| 5 | 4 | 150 | 2015-02-20 11:11:31 |
| 6 | 3 | 90 | 2015-02-20 11:12:20 |
| 7 | 4 | 100 | 2015-02-20 11:12:20 |
| 8 | 3 | 40 | 2015-02-20 11:12:55 |
| 9 | 4 | 50 | 2015-02-20 11:12:55 |
+-------------------+--------------------------+-------------------+---------------------+
So far, i have this query, but id doesn't returns correct record..
SELECT po.purchase_order_id, SUM(pod.ordered_quantity) AS Sum_of_ordered_quantity, SUM(dor.recieved_quantity) AS Sum_of_recieved_quantity
FROM purchase_order AS po
INNER JOIN purchase_order_detail AS pod ON po.purchase_order_id = pod.purchase_order_id
INNER JOIN delivery_order AS dor ON dor.purchase_order_detail_id = pod.purchase_order_detail_id
GROUP BY po.purchase_order_id
it returns this,
+-------------------+-------------------------+--------------------------+
| purchase_order_id | Sum_of_ordered_quantity | Sum_of_received_quantity |
+-------------------+-------------------------+--------------------------+
| 1 | 300 | 150 |
| 2 | 1500 | 500 |
+-------------------+-------------------------+--------------------------+
you can see in the question that , purchase_order_id 1 has 200 ordered quantity and 150 received quantity while purchase_order_id 2 has 500 ordered_quantity and 500 received quantity.
Please try this code:
SELECT po.purchase_order_id,
SUM(pod.ordered_quantity) AS Sum_of_ordered_quantity,
(SELECT `mySelect`.`desired_sum` FROM
(SELECT B.`purchase_order_id` AS myID, SUM( `A`.`recieved_quantity` ) AS desired_sum
FROM `delivery_order` AS A
LEFT JOIN `purchase_order_detail` AS B ON A.purchase_order_detail_id = B.purchase_order_detail_id
GROUP BY B.`purchase_order_id` ) AS mySelect
WHERE `mySelect`.`myID` = `po`.`purchase_order_id`) AS Sum_of_received_quantity
FROM purchase_order AS po
INNER JOIN purchase_order_detail AS pod ON po.purchase_order_id = pod.purchase_order_id
GROUP BY po.purchase_order_id
This is usual while doing aggregate sum function with multiple many-to-many tables with different joining condition.
One way is to use correlated subquery to get the aggregate value and then do the join. Something as
select
po.purchase_order_id,
pod.Sum_of_ordered_quantity,
do.Sum_of_received_quantity
from purchase_order po
join
(
select purchase_order_id,sum(ordered_quantity) as Sum_of_ordered_quantity
from purchase_order_detail
group by purchase_order_id
)pod on pod.purchase_order_id = po.purchase_order_id
join
(
select
t1.purchase_order_id,
sum(t2.recieved_quantity) as Sum_of_received_quantity
from purchase_order_detail t1
join delivery_order t2 on t1.purchase_order_detail_id = t2.purchase_order_detail_id
group by t1.purchase_order_id
)do on do.purchase_order_id = po.purchase_order_id
DEMO

Selecting random triplicates (or more) from database

I have two tables:
Students Student_Grades
V------------------------V
+----+------+ +----+------------+---------+-------+
| id | name | | id | student_id | subject | grade |
+----+------+ +----+------------+---------+-------+
| 0 | Dave | | 0 | 0 | Math | 100 |
+----+------+ +----+------------+---------+-------+
| 1 | John | | 1 | 0 | Chem | 90 |
+----+------+ +----+------------+---------+-------+
| 2 | Kate | | 2 | 0 | CompSCI | 95 |
+----+------+ +----+------------+---------+-------+
| 3 | Mimi | | 3 | 1 | ELA | 98 |
+----+------+ +----+------------+---------+-------+
| 4 | 2 | Biology | 92 |
+----+------------+---------+-------+
| 5 | 2 | Chem | 94 |
+----+------------+---------+-------+
| 6 | 2 | Math | 98 |
+----+------------+---------+-------+
| 7 | 3 | Math | 100 |
+----+------------+---------+-------+
I would like to select all subjects and grades from a random student that is enrolled in more than three subjects. (Either Dave or Kate)
Students John and Mimi would not be even considered because they are not enrolled in three subjects.
I know I can achieve this with PHP but I would like this to be done with one query to the database.
SELECT * FROM Students t JOIN (SELECT CEIL(MAX(ID)*RAND()) AS ID FROM Students) AS x ON t.ID >= x.ID LIMIT 1
With the above query, I have selected a random student, with that I can go in and check if they have three subjects with SELECT count(subjects) FROM Students WHERE id=random_id.
If the count returned is below three, then I throw away the results and run the first query again.
How would I attempt this in one query?
This is tested and working:
SELECT *
FROM Students s
JOIN (
SELECT student_id
FROM Student_Grades
GROUP BY student_id
HAVING COUNT(*) >= 3
ORDER BY RAND()
LIMIT 1
) rs
ON rs.student_id = s.id
JOIN
Student_Grades sg
ON sg.student_id = s.id
Here's the SQL Fiddle: http://sqlfiddle.com/#!2/e5b5b/1

Removing Duplicated Entries: Delete a entry that has 2 the same column

i have this table called bag:
+--------+----------+---------+----------+
| bag_id | chara_id | item_id | item_qty |
+--------+----------+---------+----------+
| 1 | 1 | 2 | 22 |
| 2 | 1 | 1 | 55 |
| 3 | 3 | 1 | 2 |
| 6 | 3 | 4 | 2 |
| 7 | 4 | 4 | 2 |
| 8 | 5 | 4 | 2 |
| 9 | 6 | 4 | 2 |
| 10 | 1 | 5 | 1 |
| 11 | 1 | 2 | 1 |
| 12 | 1 | 2 | 1 |
| 13 | 1 | 2 | 1 |
| 14 | 1 | 8 | 1 |
| 15 | 1 | 6 | 1 |
| 16 | 1 | 8 | 1 |
| 17 | 1 | 6 | 1 |
+--------+----------+---------+----------+
the relationship goes as 1 chara = many item
now i dont want 1 chara = many duplicated item.
how can i make a query that delete's the duplicated values?
like chara_id: 1 has 3 duplicated item_id: 2
i want to delete the other 2.
Not the best way to do it. But the below should definetly work:
Delete from Bag
where bag_id
not in (
select min(bag_id) from bag a,
(select chara_id, item_id
from bag group by chara_id, item_id
having count(*) > 1) b
where a.chara_id = b.chara_id and a.item_id = b.item_id
UNION
select bag_id from bag a,
(select chara_id, item_id
from bag group by chara_id, item_id
having count(*) = 1) b
where a.chara_id = b.chara_id and a.item_id = b.item_id
)
You can simply join table bag with a subquery which gets the minimum bag_id for every combination of chara_ID and item_ID. Records that have null values on any fields on the subquery are the records that will be deleted.
DELETE a
FROM bag a
LEFT JOIN
(
SELECT chara_ID, item_ID, MIN(bag_ID) min_ID
FROM bag
GROUP BY chara_ID, item_ID
) b ON a.bag_ID = b.min_ID AND
a.chara_ID = b.chara_ID AND
a.item_ID = b.item_ID
WHERE b.min_ID IS NULL
SQLFiddle Demo

Select tag with count from multiple table join containing multiple tags for each id

My table has 4 tags linked to the to each id. I want to select the position(rank) of that value(tag_name or tag_id) and the number of times the value(tag_name or tag_id) displays in that same position(rank).
Here's what it would look like in mysql:
> +--------+------------+--------+------+
> | id | tag_name | tag_id | rank |
> +--------+------------+--------+------+
> | 2345 | cookie | 2 | 1 |
> | 2345 | bar | 1 | 2 |
> | 2345 | cereal | 3 | 3 |
> | 2345 | milk | 4 | 4 |
> | 2346 | cereal | 3 | 1 |
> | 2346 | milk | 4 | 2 |
> | 2346 | cookie | 2 | 3 |
> | 2346 | hot dogs | 5 | 4 |
> | 2347 | chocolate | 6 | 1 |
> | 2347 | bar | 1 | 2 |
> +--------+------------+--------+------+
Here's what my current code looks like:
SELECT m.*, tr.tag_id, t.tag_name, #rownum:=#rownum + 1 AS rank
FROM meals AS m
RIGHT JOIN tags_rel AS tr ON tr.meal_id = m.id
JOIN tags AS t ON tr.tag_id = t.id
JOIN (SELECT #rownum:=0) AS r
ORDER BY m.id DESC
Please keep in mind that I'm actually using a pagination so my ORDER BY actually looks like this:
ORDER BY id DESC LIMIT $start_from, 12
If I select either tag_name=bar or tag_id=1 and rank=2 I should get the bellow results. Plus I would like the count of rows returned for that value.
> +--------+------------+--------+------+
> | id | tag_name | tag_id | rank |
> +--------+------------+--------+------+
> | 2345 | bar | 1 | 2 |
> | 2347 | bar | 1 | 2 |
> +--------+------------+--------+------+
Thanks!
SELECT tag_name, tag_id, rank, COUNT(tag_id) AS rankcount
FROM table
GROUP BY tag_id, rank;
results in:
+-----------+--------+------+-----------+
| tag_name | tag_id | rank | rankcount |
+-----------+--------+------+-----------+
| bar | 1 | 2 | 2 |
| cookie | 2 | 1 | 1 |
| cookie | 2 | 3 | 1 |
| cereal | 3 | 1 | 1 |
| cereal | 3 | 3 | 1 |
| milk | 4 | 2 | 1 |
| milk | 4 | 4 | 1 |
| hot dogs | 5 | 4 | 1 |
| chocolate | 6 | 1 | 1 |
+-----------+--------+------+-----------+
9 rows in set (0.00 sec)
If you want your original table enhanced by a count of lines (although that seems to be quite senseless, as every sql result returns the number of rows) you'll have to do a subquery:
SELECT id, tag_name, tag_id as t_id, rank,
(SELECT count(tag_id) FROM table WHERE tag_id = t_id) as subqueryCount
FROM table;
+------+-----------+------+------+---------------+
| id | tag_name | t_id | rank | subqueryCount |
+------+-----------+------+------+---------------+
| 2345 | cookie | 2 | 1 | 2 |
| 2345 | bar | 1 | 2 | 2 |
| 2345 | cereal | 3 | 3 | 2 |
| 2345 | milk | 4 | 4 | 2 |
| 2346 | cereal | 3 | 1 | 2 |
| 2346 | milk | 4 | 2 | 2 |
| 2346 | cookie | 2 | 3 | 2 |
| 2346 | hot dogs | 5 | 4 | 1 |
| 2347 | chocolate | 6 | 1 | 1 |
| 2347 | bar | 1 | 2 | 2 |
+------+-----------+------+------+---------------+
10 rows in set (0.00 sec)
Extending that query would result in your desired result, extended by a column where the count of rows is inserted (which is IMO still rather senseless).
SELECT id, tag_name, tag_id as t_id, rank,
(SELECT count(tag_id) FROM table WHERE tag_id = t_id) as subqueryCount
FROM table
WHERE tag_id = 1;
results in
+------+----------+------+------+---------------+
| id | tag_name | t_id | rank | subqueryCount |
+------+----------+------+------+---------------+
| 2345 | bar | 1 | 2 | 2 |
| 2347 | bar | 1 | 2 | 2 |
+------+----------+------+------+---------------+
2 rows in set (0.00 sec)
how about this:
SELECT DISTINCT meal_id as MealId, t2name as TagName, t2id as TagId, Rank
FROM
(SELECT t1id as t2id, t1name as t2name, rnk as rnk2, count(*) as Rank
FROM
tags_rel AS r,
(SELECT m1id, t1id, t1name,
#rnk := CASE WHEN #id <> m1id THEN 1 ELSE #rnk + 1 END AS rnk, #id := m1id as idx
FROM
tags_rel AS r,
(SELECT #id := 0) idx,
(SELECT #rnk := 0) rxx,
(SELECT m.id AS m1id, t.id AS t1id, name AS t1name
FROM tags_rel r
JOIN (meals AS m, tags AS t ) ON ( m.id = r.meal_id AND t.id = r.tag_id )
) AS rn
WHERE r.tag_id = t1id AND r.meal_id = m1id
ORDER BY m1id, t1name
) AS rno
WHERE r.tag_id = t1id AND r.meal_id = m1id
group BY t1id, t1name, rnk
) as rn2
join (meals, tags_rel) on (meals.id = tags_rel.meal_id and tags_rel.tag_id = t2id)
ORDER BY meal_id, t2id
which results in:
2345 bar 1 2
2345 cookie 2 1
2345 cereal 3 1
2345 milk 4 2
2346 cookie 2 1
2346 cereal 3 1
2346 milk 4 2
2346 hotdog 5 1
2347 bar 1 2
2347 chocolad 6 1

Categories