MySql Distinct for same columns - mysql

hi guys i have a data from sql like
UserID UserInfo ImageName Point
159 Ozan Işık 1575972330250.jpeg 32
558 Mert Malaveş 1578248740672.jpeg 36
68 Egemen Yılmaz 1573696212462.jpeg 82
558 Mert Malaveş 1578248740678.jpeg 36
Here is my sql
SELECT DISTINCT Users.UserID,Users.UserInfo,BestPracticeParticipant.ImageName ,BestPracticeParticipant.Point
FROM BestPracticeParticipant
JOIN Users on Users.UserID=BestPracticeParticipant.UserID
WHERE BestPracticeParticipant.BestPracticeID=1
But i need to data like this
UserID UserInfo ImageName Point
159 Ozan Işık 1575972330250.jpeg 32
558 Mert Malaveş 1578248740672.jpeg 36
68 Egemen Yılmaz 1573696212462.jpeg 82
i need same UserID rows doesnt duplicate but i cant group by because of the other veriables from my sql.
Thank you.

If you want a random matching row, you can use row_number():
SELECT u.*
FROM (SELECT u.UserID, u.UserInfo, bpp.ImageName, bpp.Point,
ROW_NUMBER() OVER (PARTITION BY u.UserID ORDER BY rand()) as seqnum
FROM BestPracticeParticipant bpp JOIN
Users u
ON u.UserID = bpp.UserID
WHERE bpp.BestPracticeID = 1
) u
WHERE seqnum = 1;
Note: You can control which row you get by modifying the ROW_NUMBER().
EDIT: The above works on MySQL 8+. If you have an older version, then a unique id in the result set can be used to do what you want.
For instance, if ImageName is unique (for a given user):
SELECT u.UserID, u.UserInfo, bpp.ImageName, bpp.Point
FROM useres u JOIN
BestPracticeParticipant bpp
ON u.UserID = bpp.UserID
WHERE bpp.BestPracticeID = 1 AND
bpp.ImageName = (SELECT MAX(bpp2.ImageName)
FROM BestPracticeParticipant bpp2
WHERE bpp2.UserID = bpp.UserID AND
bpp2.BestPracticeID = 1
);

It seems your MySQL version doesn't support window functions. So from your sample data, You may try below query -
SELECT DISTINCT U.UserID,U.UserInfo,BPP.ImageName,BPP.Point
FROM (SELECT UserID, MIN(ImageName), Point
FROM BestPracticeParticipant
GROUP BY UserID, Point) BPP
JOIN Users U on U.UserID=BPP.UserID
WHERE BPP.BestPracticeID=1

Related

SQL Query Help Conditional Join

Say I have three tables as such:
Group Table
OGID
OGC
OGCD
56
300
TAS
81
TA
CAL
Structure Table
OSID
L1D
L2D
44
56
81
Contract
ContractID
44
Im giving the ContractID and I want to create a Table that has the follow:
ContractID
Structure L1D
Structure L2D
Group OGID
Group OGC
Group OGCD
Group OGID
Group OGC
Group OGCD
44
56
81
56
300
TAS
81
TA
CAL
What would be the best way to go about this in SQL?
There is also the problem that L2D can be null and anytime I try to make INNER JOIN statements to join the tables, the NULL ones are ignore.
SELECT
Contract.ContractID, Structure.L1D, Structure L2D, Group.OGID, Group.OGC, Group.OGID, Group2.OGID, Group2.OGC, Group2.OGID,
FROM
(
SELECT Structure.OSID, Structure.L1d, Group.OGID, Group.OGC, Group.OGCD
FROM Structure
INNER JOIN Group
ON (Structure.L1D = Group.OGID)
) T1
INNER JOIN
(
SELECT Structure.OSID, Structure.L1D, Group2.OGID, Group2.OGC, Group2.OGCD
FROM Structure
INNER JOIN Group2
ON (Structure.L2D = Group2.OGID)
) T2
ON (T1.OSID = T2.OSID OR T2.OSID = NULL)
See DBFIDDLE
SELECT
s.OSID,
s.L1D,
s.L2D,
g1.OGID,
g1.OGC,
g1.OGCSD,
g2.OGID,
g2.OGC,
g2.OGCSD
FROM structure s
LEFT JOIN `group` g1 ON g1.OGID = s.L1D
LEFT JOIN `group` g2 ON g2.OGID = s.L2D
The DBFIDDLE also shows what happens when L2D has the value NULL.
P.S. Generally you should not use, or at least try to avoid, Reserved Words as table names, like GROUP.

Query to select from multiple tables MySQL

I have two tables. My task is to choose the last person comment.
db_user (db_user_id, name, balance)
db_comment (db_comment_id, db_user_id, text)
My query:
SELECT db_user.name,db_comment.text
FROM db_user INNER JOIN db_comment ON db_user.db_user_id = db_comment.db_user_id
ORDER BY db_comment.db_user_id DESC
Tried to use LIMIT but failed.
A table with values ​​has already been created here: http://sqlfiddle.com/#!9/badaf/14
My data sampling should receive the last comment (db comment.text) from each person (db_user.name).
Сondition, you cannot add new fields.
You can use the following solution using a additional JOIN:
SELECT dbu.name, dbc.text
FROM db_user dbu INNER JOIN (
SELECT MAX(db_comment_id) AS db_comment_id, db_user_id
FROM db_comment
GROUP BY db_user_id
) dbc_max ON dbu.db_user_id = dbc_max.db_user_id
INNER JOIN db_comment dbc ON dbu.db_user_id = dbc_max.db_user_id
AND dbc.db_comment_id = dbc_max.db_comment_id
ORDER BY dbu.db_user_id DESC
... or using a sub-select directly on the SELECT:
SELECT dbu.name, (
SELECT `text`
FROM db_comment dbc
WHERE dbu.db_user_id = dbc.db_user_id
ORDER BY dbc.db_comment_id DESC
LIMIT 1
) AS `text`
FROM db_user dbu
ORDER BY dbu.db_user_id DESC
demo on dbfiddle.uk
I would use a correlated subquery for filtering. In many cases, this is approach that has the best performance, especially with an index on db_comment(db_user_id, db_comment_id):
select u.name, c.text
from
db_user u
inner join db_comment c on c.db_user_id = u.db_user_id
where c.db_comment_id = (
select max(c1.db_comment_id)
from db_comment c1
where c1.db_user_id = c.db_user_id
)
This assumes that the last comment is the one that has the highest db_comment_id.
Updated demo on DB Fiddle
name text
Ivan Message3 ivan
Petr Message3 Petr
Artur Message2 Artur
John Message2 John
The last comment is the one with the highest ID. So, make sure there doesn't exist a higher one for the user:
SELECT
u.name,
c.text
FROM db_user u
JOIN db_comment c ON c.db_user_id = u.db_user_id
AND NOT EXISTS
(
SELECT *
FROM db_comment c2
WHERE c2.db_user_id = c.db_user_id
AND c2.db_comment_id > c.db_comment_id
);
Or work with a list of highest comment IDs per user:
SELECT
u.name,
c.text
FROM db_user u
JOIN db_comment c ON c.db_user_id = u.db_user_id
AND (c.db_user_id, c.db_comment_id) IN
(
SELECT db_user_id, MAX(db_comment_id)
FROM db_comment
GROUP BY db_user_id
);
As of MySQL 8 you can also use window function. E.g.:
SELECT
u.name,
c.text
FROM db_user u
JOIN
(
SELECT
db_user_id,
text,
ROW_NUMBER() OVER (PARTITION BY db_user_id ORDER BY db_comment_id DESC) AS rn
FROM db_comment
) c ON c.db_user_id = u.db_user_id AND c.rn = 1;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=2c2c67cdba80a992b593e4c74201fa61
Try it -
SELECT db_user.name,db_comment.text
FROM db_user
INNER JOIN db_comment
ON db_user.db_user_id = db_comment.db_user_id
ORDER BY db_user.db_user_id DESC, db_comment.db_comment_id desc
limit 1
OR
SELECT db_user.name,db_comment.text
FROM db_user
JOIN (
select db_comment_id as maxId, db_user_id, text
from db_comment
order by db_comment_id desc
limit 1
) as db_comment ON db_comment.db_user_id = db_user.db_user_id
ORDER BY db_user.db_user_id DESC`
if the limit is not working then you can use the MAX function and make join self table
Other approach without the need to use MAX(db_comment_id) or GROUP BY db_user_id and complety works by filtering
Query
SELECT
db_user.name
, db_comment1.text
FROM
db_comment db_comment1
LEFT JOIN
db_comment db_comment2
ON
db_comment1.db_user_id = db_comment2.db_user_id
AND
db_comment1.db_comment_id < db_comment2.db_comment_id
INNER JOIN
db_user
ON
db_comment1.db_user_id = db_user.db_user_id
WHERE
db_comment2.db_user_id IS NULL
Note: Using db_comment1.db_comment_id > db_comment2.db_comment_id instead would do MIN(db_comment_id) yes the operator direction can feel pretty bit counterintuitive and it is very easy to get wrong and write the wrong one (that's why i needed to edit mine answer...) , see demo..
Result
| name | text |
|-------|----------------|
| Ivan | Message3 ivan |
| Petr | Message3 Petr |
| Artur | Message2 Artur |
| John | Message2 John |
Performance note: it needs to have the INDEX(db_user_id, db_comment_id) on the db_comment table otherwise it will not be very fast. If you have that index, MySQL should be able to handle (very) large tables when running this query..
see demo

How to count rows that aren't match between two tables in MySQL?

I have the following tables:
Users:
ID LastPaymentDate
1 2017-01-01
2 2018-02-05
3 2018-04-06
5 NULL
ActivityLog:
ID ActivityDate
1 2017-01-01
1 2017-05-17
3 2018-05-20
I need to find out the number of users that have LastPaymentDate but doesn't have matched ActivityDate
The output result for the above data is: 2 (UserID 3 and 2).
How can I do this?
We can try using a left join approach here:
SELECT u.ID, u.LastPaymentDate
FROM Users u
LEFT JOIN ActivityLog a
ON u.ID = a.ID AND u.LastPaymentDate = a.ActivityDate
WHERE
a.ID IS NULL AND u.LastPaymentDate IS NOT NULL;
Demo
Use NOT EXISTS:
SELECT COUNT(*)
FROM Users u
WHERE
u.LastPaymentDate IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM ActivityLog a
WHERE u.ID = a.ID AND u.ActivityDate = a.ActivityDate
)
The good thing about this approach is that it will not count several times the same record in Users, even if it has several matching record in the ActivityLog.
I had a problem similar to this. My resolution was to create views to display the fields and the count as columns. Then I did a join between the views to display the net results:
CREATE ALGORITHM=UNDEFINED DEFINER=MySQL CURRENT_USER() SQL SECURITY DEFINER VIEW `subcr_count_x` AS SELECT `x_subscriptions`.`user_id` AS `user_id`, count(0) AS `cnt` FROM `x_subscriptions` WHERE (`x_subscriptions`.`user_id` > 0) GROUP BY `x_subscriptions`.`user_id` ;
CREATE ALGORITHM=UNDEFINED DEFINER=MySQL CURRENT_USER() SQL SECURITY DEFINER VIEW `subcr_count_y` AS SELECT `y_subscriptions`.`user_id` AS `user_id`, count(0) AS `cnt` FROM `y_subscriptions` WHERE (`y_subscriptions`.`user_id` > 0) GROUP BY `y_subscriptions`.`user_id`;
To select the records where there isn't a match, it does this.
SELECT * FROM
`subcr_count_x` x INNER JOIN
`subcr_count_y` y ON x.user_id = y.user_id
WHERE x.cnt != y.cnt

SELECT group by twice

I'm not strong in DB at all and I need your help.
I need SQL request with GROUP by twice.
Example of my data in table
<table border="1" style="border-collapse:collapse">
<tr><th>id</th><th>market_id</th><th>price</th><th>low</th><th>high</th><th>symbol</th><th>created_at</th></tr>
<tr><td>1</td><td>1</td><td>5773.8</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:10</td></tr>
<tr><td>2</td><td>1</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:11</td></tr>
<tr><td>3</td><td>1</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:12</td></tr>
<tr><td>4</td><td>2</td><td>5771</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:18</td></tr>
<tr><td>5</td><td>2</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:18</td></tr>
<tr><td>6</td><td>2</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:19</td></tr>
<tr><td>7</td><td>1</td><td>5773.1</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:25</td></tr>
<tr><td>8</td><td>1</td><td>0.4274</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:25</td></tr>
<tr><td>9</td><td>1</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:26</td></tr>
<tr><td>10</td><td>2</td><td>5773.1</td><td>5685</td><td>6020</td><td>btcusd</td><td>2017-10-27 16:46:32</td></tr>
<tr><td>11</td><td>2</td><td>0.42741</td><td>0.39</td><td>0.43983</td><td>iotusd</td><td>2017-10-27 16:46:32</td></tr>
<tr><td>12</td><td>2</td><td>0.20026</td><td>0.1986</td><td>0.20352</td><td>xrpusd</td><td>2017-10-27 16:46:33</td></tr></table>
I would like to get latest data for every market_id and symbol
That's mean I need somethind like that in the end :
- id market_id symbol
- 7 1 btcusd
- 8 1 iotusd
- 9 1 xrpusd
- 10 2 btcusd
- 11 2 iotusd
- 12 2 xrpusd
Really need help, a little bit blocked.
You are almost there. Try this
SELECT c.*
FROM CRYPTO as C
JOIN (
SELECT market_id, symbol, MAX(id) as maxid
FROM CRYPTO
GROUP BY market_id, symbol
) AS C2
ON C2.maxid = C.id and C.market_id = c2.market_id and c.symbol = c2.symbol
Along these lines...
SELECT MAX(id), market_id, symbol
FROM crypto
GROUP BY market_id, symbol
Here's my comment stated as SQL.
SELECT A.ID, A.MarketID, A.Symbol, A.Price, A.Low, A.High
FROM CRYPTO A
INNER JOIN (SELECT max(Created_at) MCA, Market_ID, Symbol
FROM crypto
GROUP BY Market_ID, Symbol) B
on A.Created_At = B.MCA
and A.market_ID = B.Market_ID
and A.Symbol = B.Symbol
What this does:
The derived table (aliased B) generates 1 line for each market_ID and symbol having the max created_at time. It then uses this derived table set to join back to the base set (aliased A) to limit the data to just those having the max created_at. this allows us to show the whole record from A for each unique market_Id and symbol; but only for records having the max created_at.
Other engines would allow you to use a cross apply or an analytic to obtain the desired results.
I tried these requests
SELECT * FROM CRYPTO as C3
JOIN (
SELECT MAX(id) as max
FROM CRYPTO as C1
GROUP BY symbol
) AS C2
ON C2.max = C3.id
SELECT M.id, M.name, R.symbol FROM MARKET AS M
JOIN (
SELECT DISTINCT C.symbol, C.market_id
FROM CRYPTO as C
) as R
ON M.id = R.market_id
But finally I did not find the good combination.

Multiple rows to one row query

On my database when i do this query:
SELECT *
FROM users u
LEFT JOIN usersToStrategy uts on uts.userID = u.userID
LEFT JOIN strategy s on uts.stratID = s.stratID
I get the following results:
userID username fName utsID userID stratID stratID stratName
1 nlubin Neal 66 1 4 4 s3
1 nlubin Neal 65 1 3 3 s5
1 nlubin Neal 64 1 2 2 s2
1 nlubin Neal 63 1 1 1 s1
How do I structure my query that instead of getting those four rows (for only one user) I get one row for the user with all of the user's strats in one cell in the row like this:
userID username fName stratNames
1 nlubin Neal s3, s5, s2, s1
I can give you as much information that I can within reason to help with the question.
I think you are looking for GROUP_CONCAT
SELECT userID, username, fName, GROUP_CONCAT(stratNames SEPARATOR ', ') AS strats
FROM users u
LEFT JOIN usersToStrategy uts on uts.userID = u.userID
LEFT JOIN strategy s on uts.stratID = s.stratID
GROUP BY userID, username, fName
You use GROUP_CONCAT.
Use GROUP_CONCAT like this
SELECT userID,
username,
fName,
GROUP_CONCAT(stratNames)
FROM users u
LEFT JOIN usersToStrategy uts on uts.userID = u.userID
LEFT JOIN strategy s on uts.stratID = s.stratID
GROUP BY username;