I have a table in mysql have a lot of data.i want to display only distinct and last saved data...here is what i have
CREATE TABLE amounts(
id MEDIUMINT NOT NULL AUTO_INCREMENT,
bank_id INT,
amount INT,
PRIMARY KEY (id)
) ENGINE=MyISAM;
INSERT INTO `amounts` (`id`, `bank_id`, `amount`) VALUES (NULL, '1', '100'),
(NULL, '2', '200'),(NULL, '3', '300'),(NULL, '1', '500'),(NULL, '2', '600'),
(NULL, '1', '800'),(NULL, '2', '50');
I want result like this
bank_id amount
1 800
2 50
3 300
how will be select query for this?
Use a self join. This is almost always faster than a subquery.
SELECT a.*
FROM amounts a LEFT JOIN amounts b ON a.bank_id = b.bank_id AND a.id < b.id
WHERE b.id IS NULL
See a demo
This should do it:
SELECT a.bank_id,
b.amount
FROM (SELECT bank_id,
Max(id) AS id
FROM amounts
GROUP BY bank_id) a
INNER JOIN amounts b
ON b.id = a.id
Result
| BANK_ID | AMOUNT |
--------------------
| 1 | 800 |
| 2 | 50 |
| 3 | 300 |
See a demo
You could use a subquery where you select max IDs for every bank_id, then you select all rows whose IDs are returned by this subquery:
SELECT amounts.*
FROM amounts
WHERE id IN (select max(id)
from amounts
group by bank_id)
ORDER BY bank_id
Please see this fiddle.
Related
SlNo
UserID
points
1
001
Three
2
002
Three
3
001
Three
I have the following table named 'userdata' and I would like to get the points of just the unique usernames.
SELECT points from userdata where USERID==distinct
Is there a functionality in mysql that works similar to == ?
Use Distinct
Query
Select distinct UserId, points
From userdata;
There are some possibilities
IN clause or a INNER JOIN
Latter will be faster on big tables
CREATE TABLE userdata (
`SlNo` INTEGER,
`UserID` INTEGER,
`points` VARCHAR(5)
);
INSERT INTO userdata
(`SlNo`, `UserID`, `points`)
VALUES
('1', '001', 'Three'),
('2', '002', 'Three'),
('3', '001', 'Thr');
SELECT `points` FROM userdata WHERE `UserID` IN (SELECT `UserID` FROM userdata GROUP BY `UserID` HAVING COUNT(*) = 1)
| points |
| :----- |
| Three |
SELECT `points`
FROM userdata u1 INNER JOIN (SELECT `UserID` FROM userdata GROUP BY `UserID` HAVING COUNT(*) = 1) u2 ON u1.`UserID` = u2.`UserID`
| points |
| :----- |
| Three |
db<>fiddle here
I have a JSON_ARRAY of ids in the form of [1,3,...]. Each value represents an id to a value in another table.
Table: pets
id | value
1 | cat
2 | dog
3 | hamster
Table: pet_owner
id | pets_array
1 | [1, 3]
2 | [2]
3 | []
What I want to get when I query pet_owners is the following result:
Table: pet_owner
id | pets_array
1 | ["cat", "hamster"]
2 | ["dog"]
3 | []
How do I run a sub-select on each array element to get its value?
As JSON goes, it is always a pain to handle
When you need also all that have no pets, you must left Join the owner table
CREATE TABLE pet_owner (
`id` INTEGER,
`pets_array` JSON
);
INSERT INTO pet_owner
(`id`, `pets_array`)
VALUES
('1', '[1, 3]'),
('2', '[2]'),
('3', '[]');
CREATE TABLE pets (
`id` INTEGER,
`value` VARCHAR(7)
);
INSERT INTO pets
(`id`, `value`)
VALUES
('1', 'cat'),
('2', 'dog'),
('3', 'hamster');
SELECT
t1.id,
JSON_ARRAYAGG(
p.`value`
) AS pets_array
FROM(
SELECT *
FROM pet_owner ,
JSON_TABLE(
pet_owner.pets_array , "$[*]"
COLUMNS(IDs int PATH "$" NULL ON ERROR DEFAULT '0' ON EMPTY )
) AS J_LINK ) t1
LEFT JOIN pets p ON p.id =t1.IDs
GROUP BY
t1.id
;
id | pets_array
-: | :-----------------
1 | ["cat", "hamster"]
2 | ["dog"]
db<>fiddle here
A normalized Table would spare you to convert the data into usable columns.
You can join on json_contains(), then re-aggregate:
select po.id, json_arrayagg(p.value) as owners
from pet_owner po
left join pets p on json_contains(po.pets_array, cast(p.id as char))
group by po.id
Note that, unlike most (if not all!) other databases, MySQL does not guarantee the ordering of elements in an array generated by json_arrayagg(): that's just a fact we have to live with as of the current version.
I have two tables (user and log) in mysql.
USER TABLE
id email name status
-- ------- ------ --------
1 "x#domain.com" "Carlos" 1
2 "c#domain.com" "Marie" 1
3 "k#domain.com" "Jason" 1
LOG TABLE
id time user_id
-- ------- -------
123 "2020-09-07 08:05:03" 1
124 "2020-09-07 08:32:21" 2
125 "2020-09-09 09:01:46" 1
126 "2020-09-07 11:05:03" 3
I would like to get all the users and its last log in for each one. Then, I have this query:
SELECT
user.id,
user.name,
user.email,
MAX(log.time) as time
FROM user
LEFT JOIN log on user.id = log.user_id
WHERE user.status_id = 1
GROUP BY user.id
ORDER BY log.time DESC
The query returns all the users with its log (null if there is no log for the user) but the log is not the last of the user. May somebody help me?
Thanks in advance.
There is very little you need to change to make your query work (apart from two typos - extra comma and status vs status_id):
The order by clause does not make any sense, since the resultset does not have log.time field, it only has max(log.time), so that's you need to order on and no need to use CTEs or subqueries - as long as you do not want to include additional columns from the log table:
Schema (MySQL v5.7)
CREATE TABLE `user` (
`id` INTEGER primary key,
`email` VARCHAR(14),
`name` VARCHAR(8),
`status` INTEGER
);
INSERT INTO `user`
(`id`, `email`, `name`, `status`)
VALUES
('1', 'x#domain.com', 'Carlos', '1'),
('2', 'c#domain.com', 'Marie', '1'),
('3', 'k#domain.com', 'Jason', '1');
CREATE TABLE log (
`id` INTEGER,
`time` VARCHAR(21),
`user_id` INTEGER
);
INSERT INTO log
(`id`, `time`, `user_id`)
VALUES
('123', '2020-09-07 08:05:03', '1'),
('124', '2020-09-07 08:32:21', '2'),
('125', '2020-09-09 09:01:46', '1'),
('126', '2020-09-07 11:05:03', '3');
Query #1
SELECT
user.id,
user.name,
user.email,
MAX(log.time) as time
FROM user
LEFT JOIN log on user.id = log.user_id
WHERE user.status = 1
GROUP BY user.id
ORDER BY max(log.time) DESC;
Result:
| id | name | email | time |
| --- | ------ | ------------ | ------------------- |
| 1 | Carlos | x#domain.com | 2020-09-09 09:01:46 |
| 3 | Jason | k#domain.com | 2020-09-07 11:05:03 |
| 2 | Marie | c#domain.com | 2020-09-07 08:32:21 |
If you are using MySQL 8+, then ROW_NUMBER is one sensible approach here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY time DESC) rn
FROM log
)
SELECT
u.id,
u.name,
u.email,
t.time
FROM user u
LEFT JOIN cte t ON u.id = t.user_id
WHERE
u.status = 1 AND t.rn = 1
ORDER BY
t.time DESC;
SELECT u.*, l.*
FROM user
JOIN log ON user.id = log.user_id
JOIN ( SELECT user_id, MAX(time) time
FROM log
GROUP BY user_id ) log1 ON log.user_id = log1.user_id AND log.time = log1.time
Applicable for MySQL 5.x
This question already has answers here:
How can I return pivot table output in MySQL?
(10 answers)
Closed 2 years ago.
I need to query some data from a MySQL key value table but I want get a "normal" table as result with specified keys as columns.
Example of my table USERS:
ID, user_id, key, value
----------------------------------
1 1 first_name Peter
2 1 last_name Sputnick
3 2 first_name Jan
4 2 last_name Putgraver
5 2 country Netherlands
I want this as query result:
ID, first_name, last_name
----------------------------
1 Peter Sputnick
2 Jan Putgraaf
Is there a good and efficient way to achieve this? Note that I don't want to include the country column in my result.
I need this because i need to join this data with data from another table and i don't want to use a sub query for each field i need. So I don't want to do something like this:
SELECT *,
(SELECT value
FROM users
WHERE user_id = o.user_id
AND key = first_name),
(SELECT value
FROM users
WHERE user_id = o.user_id
AND key = last_name),
FROM orders o
You can use conditional aggregation:
select
id,
max(case when key = 'fist_name' then value end) first_name,
max(case when key = 'last_name' then value end) last_name
from users
group by id
for thsi you must use conditional aggregation,
CREATE TABLE USERS (
`ID` VARCHAR(6),
`user_id` VARCHAR(9),
`key` VARCHAR(10),
`value` VARCHAR(11)
);
INSERT INTO USERS
(`ID`, `user_id`, `key`, `value`)
VALUES
('1', '1', 'first_name', 'Peter'),
('2' , '1', 'last_name', 'Sputnick'),
('3', '2', 'first_name', 'Jan'),
('4', '2', 'last_name', 'Putgraver'),
('5', '2', 'country', 'Netherlands');
SELECT user_id
, MAX(IF(`key` ='first_name',`value`,NULL )) fisrt_name
, MAX(IF(`key` ='last_name',`value`,NULL )) last_name
FROM USERS
GROUP BY user_id;
user_id | fisrt_name | last_name
:------ | :--------- | :--------
1 | Peter | Sputnick
2 | Jan | Putgraver
db<>fiddle here
If you want all keys you must use a pivot table
I have a table which contains users and some scores associated with them. something like this:
uid | username | score | time_spent
1 | test | 25 | 12
then I am sorting this table based on score and time_spent. As a result I get some kind of highscores table.
what I want to do is to assign row numbers to this sorted table to have the information about the specific users place in the highscores table and then select a specific user from this sorted table with row number.
I tried to do it like this:
SET #row_number = 0;
SELECT * FROM
(SELECT uid, username, score, time_spent, #row_number:=#row_number+1 AS row_number,
SUM(score) AS points_awarded,
MIN(time_spent) AS time
FROM results
GROUP BY uid
ORDER BY points_awarded DESC, time ASC) as t
WHERE t.uid=1
but this does not work correctly. The result row I get has always the last number of total records.
You must have the #row_number in the outer query:
SET #row_number = 0;
SELECT
t.*, #row_number:=#row_number+1 AS row_number
FROM (
SELECT
uid, username,
SUM(score) AS points_awarded,
MIN(time_spent) AS time
FROM results
GROUP BY uid, username
) t
ORDER BY t.points_awarded DESC, t.time ASC
See the demo.
INSERT INTO results
(`uid`, `username`, `score`, `time_spent`)
VALUES
('1', 'test1', '25', '12'),
('1', 'test1', '20', '13'),
('1', 'test1', '20', '11'),
('2', 'test2', '12', '17'),
('2', 'test2', '29', '16'),
('2', 'test2', '25', '15'),
('3', 'test3', '45', '18'),
('3', 'test3', '15', '69');
Results:
| uid | username | points_awarded | time | row_number |
| --- | -------- | -------------- | ---- | ---------- |
| 2 | test2 | 66 | 15 | 1 |
| 1 | test1 | 65 | 11 | 2 |
| 3 | test3 | 60 | 18 | 3 |
If you only want the position of a single user at a time, the following should work:
-- get best score and time for the user
SELECT score, time_spent
INTO #u_score, #u_time
FROM results
WHERE uid = 2
ORDER BY score DESC, time_spent ASC
LIMIT 1;
SELECT *, -- below: count "better" distinct users
(SELECT COUNT(DISTINCT uid)+1 FROM results WHERE score > #u_score
OR (score = #u_score AND time_spent < #u_time)) AS pos
FROM results
WHERE uid = 2
AND score = #u_score
AND time_spent = #u_time;
EDIT: The request below should give you the complete "leaderboard", which you can then use as subquery from to get a specific user, like you did in your example:
SET #row_number = 0;
SELECT t.*, #row_number:=#row_number+1 AS row_number
FROM (
SELECT r1.*
FROM results r1
LEFT JOIN results r2
ON r1.uid = r2.uid
AND (r1.score < r2.score
OR (r1.score = r2.score
AND r1.time_spent > r2.time_spent))
WHERE r2.uid IS NULL
ORDER BY r1.score DESC, r1.time_spent ASC
) AS t
EDIT2: I assumed each row in your table was a separate score "attempt" and that you wanted to take into consideration the best attempt of each user, but it looks like you want the sum of these scores, so forpas's answer is the one you want :)