MySQL - group by like classification - mysql

I have this table :
/* Create a table called users */
CREATE TABLE users(Id integer PRIMARY KEY, Name text, country text);
/* Create few records in this table */
INSERT INTO users VALUES(1,'Tom','Canada');
INSERT INTO users VALUES(2,'Lucy','USA');
INSERT INTO users VALUES(3,'Frank','USA');
INSERT INTO users VALUES(4,'Jane','Canada');
INSERT INTO users VALUES(5,'Robert','Italy');
COMMIT;
I want a query that displat this result :
+---+--------+--------+
| 1 | Tom | Canada |
+---+--------+--------+
| 4 | Jane | Canada |
| 3 | Frank | USA |
| 2 | Lucy | USA |
| 5 | Robert | Italy |
+---+--------+--------+
that mean classification users by countries. For that I use this query :
SELECT * FROM users group by country;
but the result is not satisfying, because the group by delete duplicate rows.
Any advice will be nice.

Your desired output looks more like a specific ordering than a grouping. For ordering use ORDER BY:
SELECT * FROM users ORDER BY country;

Related

MySQL - How to order duplicate rows in a key value pair table based on multiple columns?

So I have the following key/value pair table, where users submit data through a form and each question on the form is added to the table here as an individual row. Submission_id identifies each form submission.
+----+---------------+--------------+--------+
| id | submission_id | key | value |
+----+---------------+--------------+--------+
| 1 | 10 | manufacturer | Apple |
| 2 | 10 | model | 5s |
| 3 | 10 | firstname | Paul |
| 4 | 15 | manufacturer | Apple |
| 5 | 15 | model | 5s |
| 6 | 15 | firstname | Paul |
| 7 | 20 | manufacturer | Apple |
| 8 | 20 | model | 5s |
| 9 | 20 | firstname | Andrew |
+----+---------------+--------------+--------+
From the data above you can see that the submissions with id of 10 and 15 both have the same values (just different submission id). This is basically because a user has submitted the same form twice and so is a duplicate.
Im trying to find a way to order these table where the any duplicate submissions appear together in order. Given the above table I am trying to build a query that gives me the result as below:
+---------------+
| submission_id |
+---------------+
| 10 |
| 15 |
| 20 |
+---------------+
So I want to check to see if a submission where the manufacturer, model and firstname keys have the same value. If it does then these get the submission id and place them adjacently in the result. In the actual table there are other keys, but I only want to match duplicates based on these 3 keys (manufacturer, model, firstname).
I’ve been going back and forth to the drawing board quite some time now and have tried looking for some possible solutions but cannot get something reliable.
That's not a key value table. It's usually called an Entity-Attribute-Value table/relation/pattern.
Looking at the problem, it would be trivial if the table were laid out in conventional 1st + 2nd Normal form - you just do a join on the values, group by those and take a count....
SELECT manufacturer, model, firstname, COUNT(DISTINCT submission_id)
FROM atable
GROUP BY manufacturer, model, firstname
HAVING COUNT(DISTINCT submission_id)>1;
Or a join....
SELECT a.manufacturer, a.model, a.firstname
, a.submission_id, b.submission_id
FROM atable a
JOIN atable b
ON a.manufacturer=b.manufacturer
AND a.model=b.model
AND a.firstname=b.firstname
WHERE a.submission_id<b.submission_id
;
Or using sorting and comparing adjacent rows....
SELECT *
FROM
(
SELECT #prev.submission_id AS prev_submission_id
, #prev.manufacturer AS prev_manufacturer
, #prev.model AS prev_model
, #prev.firstname AS pref_firstname
, a.submission_id
, a.manufacturer
, a.model
, set #prev.submission_id:=a.submission_id as currsid
, set #prev.manufacturer:=a.manufacturer as currman
, set #prev.model:=a.model as currmodel
, set #prev.firstname=a.forstname as currname
FROM atable
ORDER BY manufacturer, model, firstname, submission_id
)
WHERE prev_manufacturer=manufacturer
AND prev_model=model
AND prev_firstname=firstname
AND prev_submission_id<>submission_id;
So the solution is to simply make your data look like a normal relation....
SELECT ilv.values
, COUNT(ilv.submission_id)
, GROUP_CONCAT(ilv.submission_id)
FROM
(SELECT a.submission_id
, GROUP_CONCAT(CONCAT(a.key, '=',a.value)) AS values
FROM atable a
GROUP BY a.submission_id
) ilv
GROUP BY ilv.values
HAVING COUNT(ilv.submission_id)>1;
Hopefully the join and sequence based solutions should now be obvious.

how to update table using trigger

i am new to this and want some help.i have table with name "abc" with following entries
+------+--------+------+
| Id | Name | City |
+------+--------+------+
| 101 | john | abc |
| 102 | Miller | cbz |
+------+--------+------+
and another table "xyz"
+------+--------+------+
| Id | Name | City |
+------+--------+------+
| 102 | Miller | cbz |
+------+--------+------+
i applied trigger on table "abc" which will update the table "xyz" with recently inserted values and will delete all previous entries...
for example, when i fire insert query on "abc" table i get ,"abc" as follow
insert into abc Values(103,'Joseph','xyz');
i get output for table "abc" as,
+------+--------+------+
| Id | Name | City |
+------+--------+------+
| 101 | john | abc |
| 102 | Miller | cbz |
| 103 | Joseph | xyz |
+------+--------+------+
and table "xyz" as,
+------+--------+------+
| Id | Name | City |
+------+--------+------+
| 103 | Joseph | xyz |
+------+--------+------+
now my question is how to acheive this using only one table(i dont want to use two table as this is not my requirement).
like following ..
insert into xyz values(104,'Ridhit','pqr');
+------+--------+------+
| Id | Name | City |
+------+--------+------+
| 104 | Ridhit | pqr |
+------+--------+------+
Please help.Trigger i used is
DELIMITER !!
create trigger OnlyOne BEFORE INSERT on abc
for each row
BEGIN
DECLARE a1 INT;
Select count(1) INTO a1 from xyz;
IF a1>0 THEN
delete from xyz limit 1;
insert into xyz(Id,Name,City) values (new.Id,new.Name,new.City);
ELSE
insert into xyz(Id,Name,City) values (new.Id,new.Name,new.City);
END IF;
END;
!!
DELIMITER ;
Do this on application level, but you better make sure you use transactions. You probably don't want to have an empty table, when the operation crashes in the middle.
You may also consider to have following approach.
Instead of deleting/updating whenever an insert occurs, add a column like created with default value of current timestamp to your table so that it looks something like this:
CREATE TABLE abc(
id int auto_increment primary key,
name varchar(50),
city varchar(50),
created timestamp default current_timestamp
);
To get the latest entry you just do
SELECT * FROM abc ORDER BY created DESC LIMIT 1;
Or you put this in a view
CREATE VIEW just_latest_entry_from_abc AS
SELECT * FROM abc ORDER BY created DESC LIMIT 1;
Then you just do
SELECT * FROM just_latest_entry_from_abc;
When table size matters, create a cronjob or a scheduled event to delete older entries on a regular basis.

Update query using select -- how does it know which row to update

I have the following query that is working correctly but I don't understand why. I am changing the balance column for each row..How does it know which row to update for a particular customer.
UPDATE phppos_customers SET balance =
IFNULL((SELECT SUM(transaction_amount)
FROM `phppos_store_accounts`
WHERE deleted = 0 and customer_id = phppos_customers.person_id), 0);
For the sake of example, lets say you have the following two tables
[Users]
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | patrick |
| 2 | chris |
+---------+----------+
[Names]
+---------+------------+-----------+
| user_id | first_name | last_name |
+---------+------------+-----------+
| 1 | Patrick | Stewart |
| 2 | Chris | Angel |
+---------+------------+-----------+
If you had a update query like the one in your original post, you would want to tell it how to align the two tables. If you had the clause WHERE Users.user_id = Names.user_id, you are effectively telling SQL to view the data as if both tables were aligned side by side, using the user_id in both tables to determine where they match up. This would mean the first_name and last_name in the [Names] table for user_id 1 will be what is used when updating the row in the [Users] table that is user_id 1. It is essentially viewing the data merged together, like this:
[Users] and [Names] tables aligned by the user_id columns
+---------+----------+---------+------------+-----------+
| user_id | username | user_id | first_name | last_name |
+---------+----------+---------+------------+-----------+
| 1 | patrick | 1 | Patrick | Stewart |
| 2 | chris | 2 | Chris | Angel |
+---------+----------+---------+------------+-----------+
So when SQL is doing the updating, it updates each row with the corresponding data from the other table, using this aligning to know which data to use for each rows update.
The process of aligning/merging data from multiple tables is called joining in SQL, here is some more information that illustrates how it works if you are interested.

retrieving data from two mysql tables where the second table depends on the first

I have two MySql tables:
users(id_user, name, age, gender ).
ways(#id_user,id_way, start, end, date).
What I want is to retrieve all the ways with their corresponding users details.
So my result would be like this:
id_way | start | end | date | id_user | name | age | gender
---------------------------------------------------------------------------
2 | place1 | place2 | 12/06/2013 | 145 | john | 28 | m
Have you tried JOIN?
SELECT ways.id_way, ways.start, ways.end, ways.date, users.*
FROM ways JOIN users USING (id_user)

mySql - updating by comparing rows in same table

I want to update a column by comparing each row to all other rows in the table but I cant figure out how to distinguish the column names in the row being updated with the rows being searched through.
Here's a simplified example...
people:
+--------+-----+----------------+
| name | age | nameClosestAge |
+--------+-----+----------------+
| alice | 20 | |
| bob | 30 | |
| clive | 22 | |
| duncan | 24 | |
+--------+-----+----------------+
To fill in the 'nameClosestAge' column with the name of the person that is closest in age to each person, you could do this...
create temporary table peopleTemp like people;
insert into peopleTemp select * from people;
update people set nameClosestAge =
(select name from peopleTemp where people.name != peopleTemp.name
order by abs(people.age - peopleTemp.age) asc limit 1);
Which produces this....
+--------+-----+----------------+
| name | age | nameClosestAge |
+--------+-----+----------------+
| alice | 20 | clive |
| bob | 30 | duncan |
| clive | 22 | alice |
| duncan | 25 | clive |
+--------+-----+----------------+
Surely there is a way to do this without creating a duplicate table.
I'm looking for the most efficient method here as I have a very large table and its taking too long to update.
I'm using mySql with PHP.
You could perform this with just one sub-query and no temp table.
SELECT name, age, (
SELECT name
FROM people
WHERE name != ppl.name
ORDER BY ABS( people.age - ppl.age )
LIMIT 1
) AS nameClosestAge
FROM people AS ppl;
Checked and works :)
EDIT: If you want to be able to work with the calc'ed row, you can use a view;
CREATE VIEW people_close AS
SELECT name, age, (
SELECT name
FROM people
WHERE name != ppl.name
ORDER BY ABS( people.age - ppl.age )
LIMIT 1
) AS nameClosestAge
FROM people AS ppl;
You can't update the calculated field but can query against it easily.