Here's my MySQL table:
ID | groupID | value
------------------------------
1 | 1 |
2 | 1 | 0.34353
3 | 1 |
4 | 2 |
5 | 2 | 0.23232
6 | 3 |
7 | 3 |
8 | 3 | 1.23234
9 | 3 |
I want to copy the available values from each group to ALL rows with the same groupID, so that my final table will look like this:
ID | groupID | value
------------------------------
1 | 1 | 0.34353
2 | 1 | 0.34353
3 | 1 | 0.34353
4 | 2 | 0.23232
5 | 2 | 0.23232
6 | 3 | 1.23234
7 | 3 | 1.23234
8 | 3 | 1.23234
9 | 3 | 1.23234
There is no fixed amount of how many rows one group has. How can I do this?
You can accomplish this using an update join. Join your initial table to a subquery which identifies the non NULL value for each groupID. After bringing in that information, update non NULL value columns to the value from the subquery.
UPDATE yourTable t1
INNER JOIN
(
SELECT groupID, MAX(value) AS value
FROM yourTable
GROUP BY groupID
) t2
ON t1.groupID = t2.groupID
SET t1.value = t2.value
WHERE COALESCE(t1.value, '') = ''
Update:
It appears that you may have empty string for the missing data, and/or NULL in addition to that. In this case, MAX() should still pickup on the non missing data, but I changed the WHERE clause appropriately.
Related
With the query:
SELECT TableA.ID, TableA.SensorID, TableA.Value, SensorIDs.Name, TableA.timestamp
FROM TableA
JOIN SensorIDs
ON TableA.SensorID = SensorIDs.ID // column 'Name' is in 'SensorIDs'
My result table looks like this:
ID | SensorID | Value | Name | timestamp
1 | 1 | 5 | A | 1000
2 | 2 | 10 | B | 1000
3 | 3 | 0 | C | 1000
4 | 1 | 1 | A | 2000
5 | 2 | 2 | B | 2000
6 | 3 | 6 | C | 2000
[..]
Is there a way to change my SQL query to get a table like this:
A | B | C | timestamp
5 | 10 | 0 | 1000
1 | 2 | 6 | 2000
Something with GROUP BY maybe?
EDIT: In the forseeable future there will be only these 3 values for 'Name'.
EDIT: RDBMS: MySQL-native (InnoDB), PHPMyAdmin
EDIT: Forgot to add column "SensorID" in the result.
I found the answer, by creating a PIVOT table with the tutorial I found here:
https://ubiq.co/database-blog/how-to-create-pivot-table-in-mysql/
SELECT time,
sum(IF(SensorID=1, Value, NULL)) AS Sensor1,
sum(IF(SensorID=2, Value, NULL)) AS Sensor2,
sum(IF(SensorID=3, Value, NULL)) AS Sensor3,
sum(IF(SensorID=4, Value, NULL)) AS Sensor4
FROM TableA
GROUP BY time
So assume I have this table:
+-------+-------+--------+--------+
| key | id | type | value |
+-------+-------+--------+--------+
| 1 | 1 | cool | 5 |
| 2 | 1 | uncool| 10 |
| 3 | 2 | none | 7 |
| 4 | 2 | cool | 23 |
| 5 | 2 | uncool| 4 |
| 6 | 3 | cool | 6 |
| 7 | 3 | uncool| 3 |
| 8 | 3 | unapp | 52 |
+-------+-------+--------+--------+
Note that the key column is just the unique identifier, and is not relevant for what I am trying to do.
I want to find the ids (key) where the cool type value is greater than the uncool type value, ignoring any other rows.
So the result would be
+------+
| id |
+------+
| 2 |
| 3 |
+------+
I spent a few hours with the SQL editor open, messing with HAVING and GROUP BY and haven't been able to find a solution.
Is there a way to do this without resorting to making queries and then performing checks in another language, or is this the only solution?
You can group by the id and take only those having the condition you describe.
select id
from your_table
group by id
having max(case when `type` = 'cool' then `value` end) >
max(case when `type` = 'uncool' then `value` end)
SELECT t1.id
FROM table t1
LEFT JOIN table t2 ON t1.id = t2.id
AND t2.type = 'uncool'
WHERE t1.type = 'cool'
AND (t1.value > t2.value OR t2.value IS NULL)
fiddle
If uncool is not present (for example, remove row with key=5) then my solution will give 2 rows whereas another solutions will give 1 row only.
I have the following query:
SELECT
items.*
FROM
`items`
INNER JOIN
`users` ON `items`.`owner` = `users`.`id`
GROUP BY
`items`.`owner`
LIMIT
10
I ensures it is grouped by the user (only one item fetched per user), but I also wish ensure that items with the category, say, "1" only appears once.
But that does not work. Well, query succeeds, but it does not group by category. Multiple categories is still shown. Any ideas?
I have created a SQLFiddle here: http://sqlfiddle.com/#!2/0a4bad/1
Instead of outputting:
+----+----------+-------+
| ID | CATEGORY | OWNER |
+----+----------+-------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 4 |
| 5 | 2 | 5 |
+----+----------+-------+
It should be outputting:
+----+----------+-------+
| ID | CATEGORY | OWNER |
+----+----------+-------+
| 1 | 1 | 1 |
| 4 | 2 | 2 |
| 5 | 2 | 4 |
| 5 | 2 | 5 |
| 8 | 3 | 3 |
+----+----------+-------+
(notice category 1 is only shown ONCE).
I want to ensure that only one item per owner is shown, and then adtionally ensure that a specific category (say 1 and 5) is only shown once. The category 1 and 5 are overpopulated, and if they are not limited, they will be 90% of the output.
You can use DISTINCT to retrieve unique data:
SELECT DISTINCT items.category
select * from items t1
where category not in (1,2)
or not exists (
select 1 from items t2
where t2.id < t1.id
and t2.category = t1.category
)
group by owner
http://sqlfiddle.com/#!2/0a4bad/27
I have the following table structure:
item_id | value |
==================
1 | 1 |
1 | 3 |
1 | 4 |
2 | 2 |
2 | 3 |
2 | 4 |
2 | 5 |
3 | 1 |
3 | 5 |
3 | 6 |
4 | 1 |
4 | 3 |
4 | 4 |
4 | 5 |
I have a query that returns those item_id whose value matches with 1, 3 and 4.
So here, the item_ids that should be returned are 1 and 4.
My query:
select item_id from table t
where exists (select item_id from table t1 where value = 1 and t1.item_id = t.item_id)
and exists (select item_id from table t1 where value = 2 and t1.item_id = t.item_id) group by item_id
This query is working fine. Here i am matching only 3 values. What if i want to match 50 such values from the table? (all the 50 values are stored in a php array) The query will be huge and also i want to do the same thing from two different tables in the same query. So, this will double the size of an already huge query. Please suggest me some other way around.
Edited::
table 2
--------
item_id | user_id |
==================
1 | 1 |
1 | 5 |
1 | 7 |
2 | 2 |
2 | 3 |
2 | 4 |
2 | 5 |
3 | 1 |
3 | 5 |
3 | 6 |
4 | 1 |
4 | 3 |
4 | 4 |
4 | 5 |
Now, i want item_id where values from table1 are 1,3,4 and user_id from table2 are 1,5,7
This problem is called Relational Division.
SELECT item_ID
FROM tableName
WHERE value IN (1,3,4)
GROUP BY item_ID
HAVING COUNT(*) = 3
if uniqueness was not enforce on column value for every item_id, DISTINCT is required to count only unique values,
SELECT item_ID
FROM tableName
WHERE value IN (1,3,4)
GROUP BY item_ID
HAVING COUNT(DISTINCT value) = 3
SQLFiddle Demo (both query included)
SQL of Relational Division
I have a few groups of data. Each group has a some property field.
For example:
_________________________
| id | value | property |
--------------------------
| 1 | 2 | 3 |
--------------------------
| 2 | 2 | 3 |
--------------------------
| 3 | 2 | 3 |
--------------------------
| 4 | 2 | 4 |
-------------------------
| 5 | 2 | 4 |
--------------------------
| 6 | 2 | 4 |
--------------------------
How can I update two strings ordered by id ASC with property = 3, and 2 strings ordered by id ASC with property = 4 by one query?
I want to update 2 of 3 rows with property = 3 and update 2 of 3 rows with property = 4. For example: rows with id 1 and 2, and rows with id 4 and 5
i.e. i want update groups of data with different conditions by one query
You can do it using calculated rank field, e.g. -
SELECT p1.*, COUNT(*) rank FROM properties p1
LEFT JOIN properties p2
ON p2.property = p1.property AND p2.id <= p1.id
GROUP BY p1.property, p1.id
This query will return dataset with row-number by property:
+------+-------+----------+------+
| id | value | property | rank |
+------+-------+----------+------+
| 1 | 2 | 3 | 1 |
| 2 | 2 | 3 | 2 |
| 3 | 2 | 3 | 3 |
| 4 | 2 | 4 | 1 |
| 5 | 2 | 4 | 2 |
| 6 | 2 | 4 | 3 |
+------+-------+----------+------+
Then you should update records with rank < 3:
UPDATE properties p
JOIN (SELECT p1.*, COUNT(*) rank FROM properties p1
LEFT JOIN properties p2
ON p2.property = p1.property AND p2.id <= p1.id
GROUP BY p1.property, p1.id) r
ON p.id = r.id
SET p.value = 100 -- set new value here
WHERE r.rank < 3
Here's the solution, and see discussion following:
update
t,
(select GROUP_CONCAT(ids) as matching_ids from (
select
SUBSTRING_INDEX(GROUP_CONCAT(id order by id), ',', 2) AS ids
from
t
where
property in (3,4)
group by
property
) s1
) s2
set value=12345
where
FIND_IN_SET(id, matching_ids) > 0
;
To illustrate, and assuming your table is called t, and the initial state is:
root#mysql-5.1.51> select * from t;
+----+-------+----------+
| id | value | property |
+----+-------+----------+
| 1 | 2 | 3 |
| 2 | 2 | 3 |
| 3 | 2 | 3 |
| 4 | 2 | 4 |
| 5 | 2 | 4 |
| 6 | 2 | 4 |
+----+-------+----------+
The result of running this query is:
root#mysql-5.1.51> select * from t;
+----+-------+----------+
| id | value | property |
+----+-------+----------+
| 1 | 12345 | 3 |
| 2 | 12345 | 3 |
| 3 | 2 | 3 |
| 4 | 12345 | 4 |
| 5 | 12345 | 4 |
| 6 | 2 | 4 |
+----+-------+----------+
A brief explanation of the query:
I pick up the first two ids for each property using the SUBSTRING_INDEX(GROUP_CONCAT(id order by id), ',', 2) statement.
I combine the above using GROUP_CONCAT(ids) as matching_ids to get all valid ids.
Finally, I update all rows in the table where the id is within combined matching_ids text.
Notes:
You should verify your group_concat_max_len variable is long enough. Default is 1024. You most probably want to have this in the millions, anyhow (regardless of my answer).
The query is far from being optimal. It answers your question, but you can't have an optimal query here.
You are most probably better off with a transaction containing two or three queries.
Good luck!
I'm assuming you mean to limit your two updates to two rows each. You can use ORDER BY and LIMIT in your update statements:
UPDATE yourtable
SET property = 'new_value'
WHERE value=2 AND property = 4
ORDER BY id ASC LIMIT 2
UPDATE yourtable
SET property = 'new_value'
WHERE value=2 AND property = 3
ORDER BY id DESC LIMIT 2
Update:
To force this into one query, you would need to JOIN against a subquery which retrieves the ids to update via UNION. I think this is legal:
UPDATE yourtable
JOIN (
(SELECT id FROM yourtable WHERE value=2 AND property=4 ORDER BY id ASC LIMIT 2)
UNION ALL
(SELECT id FROM yourtable WHERE value=2 AND property=3 ORDER BY id DESC LIMIT 2)
) updaterows ON yourtable.id = updaterows.id
SET property = 'new value'