I have a table like this:
// mytable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 10 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 5 | jack | 1 | 5 |
| 6 | jack | 1 | 10 |
| 7 | bert | 4 | 2 |
| 8 | peter | 2 | 10 |
| 9 | bert | 4 | 5 |
+----+--------+-------+-------+
Now I want to sum the numbers of value where both name and key are identical. So, I want this output:
// mynewtable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 25 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
+----+--------+-------+-------+
Is it possible to I do that?
Edit: How can I do that for insert?
// mytable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 25 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
+----+--------+-------+-------+
Inserting these rows:
+----+--------+-------+-------+
| 10 | jack | 1 | 5 |
+----+--------+-------+-------+
+----+--------+-------+-------+
| 11 | bert | 1 | 2 |
+----+--------+-------+-------+
What I want: (output)
// mynewtable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 30 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
| 11 | bert | 1 | 2 |
+----+--------+-------+-------+
You have to group by more columns.
select name, key, sum(value) from mytable group by name, key;
Group by name, key
select name, key, sum(value) as value
from mytable group by name,key
check this
CREATE TABLE #testing_123
([id] int, [name] varchar(5), [key] int, [value] int)
;
INSERT INTO #testing_123
([id], [name], [key], [value])
VALUES
(1, 'jack', 1, 10),
(2, 'peter', 1, 5),
(3, 'jack', 2, 5),
(4, 'ali', 1, 2),
(5, 'jack', 1, 5),
(6, 'jack', 1, 10),
(7, 'bert', 4, 2),
(8, 'peter', 2, 10),
(9, 'bert', 4, 5)
;
query used was
select min(id) id ,name,[key],sum(value) value from #testing_123 group by name,[key] order by 1
output after insert
For the first part (to get the id column in the way requested), you could work along:
INSERT INTO mynewtable
(id, name, `key`, `value`)
SELECT
MIN(id), name, `key`, SUM(`value`)
FROM mytable
GROUP BY name, `key`
;
Now, provided mynewtable is defined with a unique index on name and key like
CREATE TABLE mynewtable
(id INT, name VARCHAR(5), `key` INT, `value` INT, UNIQUE (name, `key`));
you'd get the requested result with
INSERT INTO mynewtable
(id, name, `key`, `value`)
VALUES
(10, 'jack', 1, 5),
(11, 'bert', 1, 2)
ON DUPLICATE KEY UPDATE `value` = `value` + VALUES(`value`)
;
Beware:
It requires the unique index on name and key to work.
It might not work correctly, if there are other unique indexes and/or a primary key on the same table as well.
NB:
Please try to avoid the use of reserved words such as value and key for, e.g., column names.
Related
I have the following table and I am trying to create histogram style buckets through MySQL.
| Id | Values |
| -------- | --------- |
| 1 | 5 |
| 2 | 7 |
| 3 | 9 |
| 4 | 11 |
| 5 | 15 |
| 6 | 31 |
| 7 | 32 |
| 8 | 43 |
What I am trying to achieve is as following:
| bucket | count |
| -------- | --------- |
| 0-9 | 3 |
| 10-19 | 2 |
| 20-29 | 0 |
| 30-39 | 2 |
| 40-49 | 1 |
Does anyone know how we can get this in a clean way?
One possible way is to create a reference table for the bucket list then LEFT JOIN it with your table. Try the following steps.
Create a table bucket_list for example:
CREATE TABLE bucket_list (
id INT NOT NULL AUTO_INCREMENT,
startno INT,
endno INT,
PRIMARY KEY(id));
Insert values into bucket_list:
INSERT INTO bucket_list (startno, endno)
VALUES
(0, 9),
(10, 19),
(20, 29),
(30, 39),
(40, 49),
(50, 59),
(60, 69),
(70, 79);
Create a query to return expected result:
SELECT CONCAT(a.startno,'-',a.endno) AS bucket,
SUM(CASE WHEN b.val IS NULL THEN 0 ELSE 1 END) AS COUNT
FROM bucket_list a
LEFT JOIN mytable b ON b.val BETWEEN a.startno AND a.endno
GROUP BY bucket;
Here's a fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=7fee426efa2b1f1e39377bb7beb68b62
And I want my output like the following:
My requirement is the following:
latest service ID
default = 'Y'
How should I write the query? Can someone help me?
You could use this:
select se.svcid, p1.pname as serviceprovider, p2.pname as tmidprovider
from tmid t
inner join (select sno, max(svcid) as maxsvcid from service group by sno) s
on t.sno = s.sno and t.`default` = 'Y'
inner join service se on se.svcid = s.maxsvcid
left join provider p1 on se.pid = p1.pid
left join provider p2 on t.pid = p2.pid;
Table
create table service (svcid int, sno varchar(20), pid int);
insert into service values
(1, '11-11-11-11', 1), (2, '11-11-11-11', 2), (3, '11-11-11-12', 1), (4, '11-11-11-12', 2), (5, '11-11-11-13', NULL);
create table tmid (id int, sno varchar(20), pid int, `default` char(1));
insert into tmid values
(1, '11-11-11-11', 1, 'N'),(2, '11-11-11-11', 2, 'Y'),(3, '11-11-11-12', 1, 'N'),
(4, '11-11-11-12', 2, 'Y'),(5, '11-11-11-13', 2, 'Y'),(6, '11-11-11-13', 3, 'N');
create table provider (pid int, pname varchar(20));
insert into provider values (1, 'Ambank'), (2, 'Citybank'), (3, 'CIMB Bank');
Data
mysql> select * from service;
+-------+-------------+------+
| svcid | sno | pid |
+-------+-------------+------+
| 1 | 11-11-11-11 | 1 |
| 2 | 11-11-11-11 | 2 |
| 3 | 11-11-11-12 | 1 |
| 4 | 11-11-11-12 | 2 |
| 5 | 11-11-11-13 | NULL |
+-------+-------------+------+
mysql> select * from tmid;
+------+-------------+------+---------+
| id | sno | pid | default |
+------+-------------+------+---------+
| 1 | 11-11-11-11 | 1 | N |
| 2 | 11-11-11-11 | 2 | Y |
| 3 | 11-11-11-12 | 1 | N |
| 4 | 11-11-11-12 | 2 | Y |
| 5 | 11-11-11-13 | 2 | Y |
| 6 | 11-11-11-13 | 3 | N |
+------+-------------+------+---------+
mysql> select * from provider;
+------+-----------+
| pid | pname |
+------+-----------+
| 1 | Ambank |
| 2 | Citybank |
| 3 | CIMB Bank |
+------+-----------+
Result
+-------+-----------------+--------------+
| svcid | serviceprovider | tmidprovider |
+-------+-----------------+--------------+
| 2 | Citybank | Citybank |
| 4 | Citybank | Citybank |
| 5 | NULL | Citybank |
+-------+-----------------+--------------+
I want my tables to output something like this
---------------------------------------------------------------------------------------------
| date | location | time | delegate 1 | delegate 2 |
|--------------------------------------------------------------------------------------------
| 2015-12-07 | Table 1 | 9:00 | first_name_4 last_name_4 | first_name_5 last_name_5 |
|--------------------------------------------------------------------------------------------
| | 9:30 | first_name_4 last_name_4 | first_name_6 last_name_6 |
|--------------------------------------------------------------------------------------------
| | 9:30 | first_name_3 last_name_3 | first_name_7 last_name_7 |
|--------------------------------------------------------------------------------------------
| | 9:00 | first_name_3 last_name_3 | first_name_7 last_name_7 |
|--------------------------------------------------------------------------------------------
Here are the tables on my db
meetings table
-------------------------------------------------------------------------------------------------
| id | date_id | time_id | location_id | delegate_id_1 | delegate_id_2 | status |
|------------------------------------------------------------------------------------------------
| 1 | 1 | 1 | 1 | 4 | 5 | A |
|------------------------------------------------------------------------------------------------
| 2 | 1 | 2 | 1 | 4 | 6 | A |
|------------------------------------------------------------------------------------------------
| 3 | 1 | 1 | 1 | 2 | 6 | P |
|------------------------------------------------------------------------------------------------
| 4 | 1 | 2 | 1 | 1 | 3 | A |
|------------------------------------------------------------------------------------------------
| 5 | 1 | 1 | 1 | 1 | 3 | A |
|------------------------------------------------------------------------------------------------
users table
-----------------------------------------
| id | first_name | last_name |
|----------------------------------------
| 1 | first_name_1 | last_name_1 |
|----------------------------------------
| 2 | first_name_2 | last_name_2 |
|----------------------------------------
| 3 | first_name_3 | last_name_3 |
|----------------------------------------
| 4 | first_name_4 | last_name_4 |
|----------------------------------------
| 5 | first_name_5 | last_name_5 |
|----------------------------------------
| 6 | first_name_6 | last_name_6 |
|----------------------------------------
locations table
-----------------------------
| id | location_name |
|----------------------------
| 1 | Table 1 |
|----------------------------
time table
-------------------------
| id | meeting_time |
|------------------------
| 1 | 9:00:00 |
|------------------------
| 1 | 9:30:00 |
|------------------------
dates table
-------------------------
| id | meeting_date |
|------------------------
| 1 | 2015-12-07 |
|------------------------
| 2 | 2015-12-08 |
|------------------------
| 3 | 2015-12-09 |
|------------------------
My initial query goes like this
-- $query_date
SELECT meeting_date
FROM dates
WHERE meeting_date = '2015-12-07'
-- $query_location
SELECT location_name.location
from location
LEFT JOIN meetings
ON meetings.location_id=location.id
LEFT JOIN date
ON meetings.date_id=date.id
WHERE meeting_date.dates = '2015-12-07'
Now, here's the part where I got it wrong.
-- $query_final
SELECT meeting_time.time, delegate1.first_name AS first_name_1,
delegate1.last_name AS last_name_1, delegate2.first_name AS first_name_2,
delegate2.last_name AS last_name_2
FROM meetings
INNER JOIN users delegate1
ON meetings.delegate_id_1=users.id
LEFT JOIN users delegate2
ON meetings.delegate_id_2=users.id
WHERE meetings.status='A'
The results on my last query give me unexpected results since the results show more entries than my meetings table.
I know the queries I made are costly but I don't know how to make a more optimized query. I don't even know if it's possible to get the results into a single query only. Any help well do. Thanks.
You can bring back everything with a single query with the right JOIN.
Be Careful, when you use column name on SQL, the syntax is TABLE.COLUMN_NAME, it seem you mistake on the order quit often...
I changed some table name as you sometime use an s at the end and sometime no.
As time and date are SQL keyword, it's better with s everywhere
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE meetings (`id` int, `date_id` int, `time_id` int, `location_id` int, `delegate_id_1` int, `delegate_id_2` int, `status` varchar(1));
INSERT INTO meetings (`id`, `date_id`, `time_id`, `location_id`, `delegate_id_1`, `delegate_id_2`, `status`)
VALUES (1, 1, 1, 1, 4, 5, 'A'),
(2, 1, 2, 1, 4, 6, 'A'),
(3, 1, 1, 1, 2, 6, 'P'),
(4, 1, 2, 1, 1, 3, 'A'),
(5, 1, 1, 1, 1, 3, 'A');
CREATE TABLE users (`id` int, `first_name` varchar(12), `last_name` varchar(11));
INSERT INTO users (`id`, `first_name`, `last_name`)
VALUES (1, 'first_name_1', 'last_name_1'),
(2, 'first_name_2', 'last_name_2'),
(3, 'first_name_3', 'last_name_3'),
(4, 'first_name_4', 'last_name_4'),
(5, 'first_name_5', 'last_name_5'),
(6, 'first_name_6', 'last_name_6');
CREATE TABLE locations (`id` int, `location_name` varchar(7));
INSERT INTO locations (`id`, `location_name`)
VALUES (1, 'Table 1');
CREATE TABLE times (`id` int, `meeting_time` varchar(7));
INSERT INTO times (`id`, `meeting_time`)
VALUES (1, '9:00:00'),
(2, '9:30:00') ;
CREATE TABLE dates (`id` int, `meeting_date` varchar(10)) ;
INSERT INTO dates (`id`, `meeting_date`)
VALUES (1, '2015-12-07'),
(2, '2015-12-08'),
(3, '2015-12-09') ;
Query 1:
-- $query_final
SELECT locations.location_name,
`times`.meeting_time,
delegate1.first_name AS first_name_1,
delegate1.last_name AS last_name_1,
delegate2.first_name AS first_name_2,
delegate2.last_name AS last_name_2
FROM meetings
LEFT JOIN locations
ON meetings.location_id=locations.id
LEFT JOIN dates
ON meetings.date_id=`dates`.id
LEFT JOIN times
ON meetings.time_id=`times`.id
INNER JOIN users delegate1
ON meetings.delegate_id_1 = delegate1.id
LEFT JOIN users delegate2
ON meetings.delegate_id_2 = delegate2.id
WHERE
meetings.status = 'A'
AND dates.meeting_date = '2015-12-07'
Results:
| location_name | meeting_time | first_name | last_name | first_name | last_name |
|---------------|--------------|--------------|-------------|--------------|-------------|
| Table 1 | 9:00:00 | first_name_1 | last_name_1 | first_name_3 | last_name_3 |
| Table 1 | 9:30:00 | first_name_1 | last_name_1 | first_name_3 | last_name_3 |
| Table 1 | 9:00:00 | first_name_4 | last_name_4 | first_name_5 | last_name_5 |
| Table 1 | 9:30:00 | first_name_4 | last_name_4 | first_name_6 | last_name_6 |
I've following MySQL tables:
memo_words
+----+-----------------------+---------+
| id | current_definition_id | word_id |
+----+-----------------------+---------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 2 |
+----+-----------------------+---------+
words
+----+
| id |
+----+
| 1 |
| 2 |
+----+
definitions
+----+
| id |
+----+
| 1 |
| 2 |
+----+
definitions_words
+---------+---------------+----------+
| word_id | definition_id | position |
+---------+---------------+----------+
| 1 | 1 | 0 |
| 1 | 2 | 0 |
| 2 | 1 | 0 |
| 2 | 2 | 0 |
+---------+---------------+----------+
I'd like for any rows returned from query below update related (definition_id, word_id) definitions_words.position with value alias position:
SELECT word_id, current_definition_id as definition_id, COUNT(*) as position
FROM memo_words
WHERE current_definition_id IS NOT NULL
GROUP BY current_definition_id, word_id;
+---------+---------------+----------+
| word_id | definition_id | position |
+---------+---------------+----------+
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 1 | 2 | 1 |
| 2 | 2 | 1 |
+---------+---------------+----------+
IN ONE QUERY.
So result this operation should looks like:
definitions_words
+---------+---------------+----------+
| word_id | definition_id | position |
+---------+---------------+----------+
| 1 | 1 | 1 |
| 1 | 2 | 1 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
+---------+---------------+----------+
DDL:
CREATE TABLE memo_words( id int, current_definition_id int, word_id int, PRIMARY KEY(id));
INSERT INTO memo_words VALUES (1, 1, 1), (2, 2, 1), (3, 1, 2), (4, 2, 2);
CREATE TABLE words(id int, PRIMARY KEY(id));
INSERT INTO words VALUES(1), (2);
CREATE TABLE definitions(id int, PRIMARY KEY(id));
INSERT INTO definitions VALUES(1), (2);
CREATE TABLE definitions_words(word_id int, definition_id int , position int);
INSERT INTO definitions_words VALUES(1,1,0), (1,2,0), (2,1,0), (2,2,0);
I would do this using a join and subquery:
update definitions_words dw join
(SELECT word_id, current_definition_id as definition_id, COUNT(*) as position
FROM memo_words
WHERE current_definition_id IS NOT NULL
GROUP BY current_definition_id, word_id
) mw
on dw.word_id = mw.word_id and dw.definition_id = mw.definition_id
set dw.position = mw.position;
UPDATE definitions_words d
JOIN (SELECT word_id, current_definition_id, COUNT(*) as position
FROM memo_words
WHERE current_definition_id IS NOT NULL
GROUP BY current_definition_id, word_id) m
ON d.word_id = m.word_id AND d.definition_id = m.current_definition_id
SET d.position = m.position
SQLFIDDLE
INSERT IGNORE INTO `PREFIX_tab_lang` (`id_tab`, `id_lang`, `name`)
(SELECT `id_tab`, id_lang, (SELECT tl.`name`
FROM `PREFIX_tab_lang` tl
WHERE tl.`id_lang` = (SELECT c.`value`
FROM `PREFIX_configuration` c
WHERE c.`name` = 'PS_LANG_DEFAULT' LIMIT 1) AND tl.`id_tab`=`PREFIX_tab`.`id_tab`)
FROM `PREFIX_lang` CROSS JOIN `PREFIX_tab`);
It's from an opensource project,and no documentation available.
Especially,what does cross-join mean? I've only used join/left join .
According to the MySQL documentation, it's basically a synonym for INNER JOIN, and INNER JOIN is the same as just JOIN (that is, "INNER" is the default).
Cross-join: http://en.wikipedia.org/wiki/Join_%28SQL%29#Cross_join
The query inserts into PREFIX_tab_lang the results of a select. The select is just two columns from the cross-product. The third column -- name -- actually comes from a totally different select, which is also pretty straight-forward except that one of it's where conditions is yet another select.
In short, this is one of the worst queries I've ever seen. It's preformance is probably horrible, and it should be replaced by a bit of TRANSATION-protected code or, at the very least, a stored procedure.
You can actually consider the following queries to be synonyms in MySQL:
SELECT *
FROM Table1
CROSS JOIN Table2;
SELECT *
FROM Table1, Table2;
SELECT *
FROM Table1
INNER JOIN Table2;
SELECT *
FROM Table1
JOIN Table2;
Test Case:
CREATE TABLE Table1 (id int, value varchar(10));
CREATE TABLE Table2 (id int, t1_id int);
INSERT INTO Table1 VALUES (1, 'Value 1');
INSERT INTO Table1 VALUES (2, 'Value 2');
INSERT INTO Table1 VALUES (3, 'Value 3');
INSERT INTO Table1 VALUES (4, 'Value 4');
INSERT INTO Table2 VALUES (1, 1);
INSERT INTO Table2 VALUES (2, 1);
INSERT INTO Table2 VALUES (3, 2);
INSERT INTO Table2 VALUES (4, 2);
INSERT INTO Table2 VALUES (5, 2);
INSERT INTO Table2 VALUES (6, 3);
INSERT INTO Table2 VALUES (7, 4);
INSERT INTO Table2 VALUES (8, 4);
INSERT INTO Table2 VALUES (9, 4);
All four queries would return the following result set:
+------+---------+------+-------+
| id | value | id | t1_id |
+------+---------+------+-------+
| 1 | Value 1 | 1 | 1 |
| 2 | Value 2 | 1 | 1 |
| 3 | Value 3 | 1 | 1 |
| 4 | Value 4 | 1 | 1 |
| 1 | Value 1 | 2 | 1 |
| 2 | Value 2 | 2 | 1 |
| 3 | Value 3 | 2 | 1 |
| 4 | Value 4 | 2 | 1 |
| 1 | Value 1 | 3 | 2 |
| 2 | Value 2 | 3 | 2 |
| 3 | Value 3 | 3 | 2 |
| 4 | Value 4 | 3 | 2 |
| 1 | Value 1 | 4 | 2 |
| 2 | Value 2 | 4 | 2 |
| 3 | Value 3 | 4 | 2 |
| 4 | Value 4 | 4 | 2 |
| 1 | Value 1 | 5 | 2 |
| 2 | Value 2 | 5 | 2 |
| 3 | Value 3 | 5 | 2 |
| 4 | Value 4 | 5 | 2 |
| 1 | Value 1 | 6 | 3 |
| 2 | Value 2 | 6 | 3 |
| 3 | Value 3 | 6 | 3 |
| 4 | Value 4 | 6 | 3 |
| 1 | Value 1 | 7 | 4 |
| 2 | Value 2 | 7 | 4 |
| 3 | Value 3 | 7 | 4 |
| 4 | Value 4 | 7 | 4 |
| 1 | Value 1 | 8 | 4 |
| 2 | Value 2 | 8 | 4 |
| 3 | Value 3 | 8 | 4 |
| 4 | Value 4 | 8 | 4 |
| 1 | Value 1 | 9 | 4 |
| 2 | Value 2 | 9 | 4 |
| 3 | Value 3 | 9 | 4 |
| 4 | Value 4 | 9 | 4 |
+------+---------+------+-------+
36 rows in set (0.01 sec)