Following query...
SELECT event_id, user_id FROM EventUser WHERE user_id IN (1, 2)
...gives me the following result:
+----------+---------+
| event_id | user_id |
+----------+---------+
| 3 | 1 |
| 2 | 1 |
| 1 | 1 |
| 5 | 1 |
| 4 | 1 |
| 6 | 1 |
| 4 | 2 |
| 2 | 2 |
| 1 | 2 |
| 5 | 2 |
+----------+---------+
Now, I want to modify the above query so that I only get for example two rows for each user_id, eg:
+----------+---------+
| event_id | user_id |
+----------+---------+
| 3 | 1 |
| 2 | 1 |
| 4 | 2 |
| 5 | 2 |
+----------+---------+
I am thinking about something like this, which of course does not work:
SELECT event_id, user_id FROM EventUser WHERE user_id IN (1, 2) LIMIT 2 by user_id
Ideally, this should work with offsets as well because I want to use it for paginations.
For performance reasons it is essential to use the WHERE user_id IN (1, 2) part of the query.
One method -- assuming you have at least two rows for each user -- would be:
(select min(event_id) as event_id, user_id
from t
where user in (1, 2)
group by user_id
) union all
(select max(event_id) as event_id, user_id
from t
where user in (1, 2)
group by user_id
);
Admittedly, this is not a "general" solution, but it might be the simplest solution for what you want.
If you want the two biggest or smallest, then an alternative also works:
select t.*
from t
where t.user_id in (1, 2) and
t.event_id >= (select t2.event_id
from t t2
where t2.user_id = t.user_id
order by t2.event_id desc
limit 1, 1
);
Here is a dynamic example for such problems, Please note that this example is working in SQL Server, could not try on mysql for now. Please let me know how it works.
CREATE TABLE mytable
(
number INT,
score INT
)
INSERT INTO mytable VALUES ( 1, 100)
INSERT INTO mytable VALUES ( 2, 100)
INSERT INTO mytable VALUES ( 2, 120)
INSERT INTO mytable VALUES ( 2, 110)
INSERT INTO mytable VALUES ( 3, 120)
INSERT INTO mytable VALUES ( 3, 150)
SELECT *
FROM mytable m
WHERE
(
SELECT COUNT(*)
FROM mytable m2
WHERE m2.number = m.number AND
m2.score >= m.score
) <= 2
How about this?
SELECT event_id, user_id
FROM (
SELECT event_id, user_id, row_number() OVER (PARTITION BY user_id) AS row_num
FROM EventUser WHERE user_id in (1,2)) WHERE row_num <= n;
And n can be whatever
Later but help uses a derived table and the cross join.
For the example in this post the query will be this:
SELECT
#row_number:=CASE
WHEN #user_no = user_id
THEN
#row_number + 1
ELSE
1
END AS num,
#user_no:=user_id userid, event_id
FROM
EventUser,
(SELECT #user_no:=0,#row_number:=0) as t
group by user_id,event_id
having num < 3;
More information in this link.
Related
How can I remove duplicates in the following order
1 2
2 1
1 3
4 1
and only output
1 2
1 3
4 1
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,user1 INT NOT NULL
,user2 INT NOT NULL
);
INSERT INTO my_table (user1,user2) VALUES
(1, 2),
(2, 1),
(1, 3),
(4, 1);
SELECT x.*
FROM my_table x
JOIN
( SELECT MIN(id) id
FROM my_table
GROUP
BY LEAST(user1,user2)
, GREATEST(user1,user2)
) y
ON y.id = x.id;
+----+-------+-------+
| id | user1 | user2 |
+----+-------+-------+
| 1 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 4 | 1 |
+----+-------+-------+
Use GROUP BY to merge duplicate.
You can use the DISTINCT clause to remove duplicates. The DISTINCT command in SQL retrieves unique data from your table. To remove duplicates, you can issue a statement similar to below:
SELECT DISTINCT Column_Name
FROM Table_Name
OUTPUT: 1 2 OR 2 1
Please let me know if this helps.
It's been asked before, but I can't get it to work properly. The selected answer doesn't work with duplicate values. The second answer should be able to handle duplicates according to the poster, but it's not functioning correctly with my data.
What I want to achieve is pretty simple:
I have a database containing all scores of all users. I want to build a highscore table, so I want to select all highscore rows of each user. With highscore row I mean the row for that user where his score is the highest.
Here's a demo I made based on the answer I mentioned at the top:
CREATE TABLE test(
score INTEGER,
user_id INTEGER,
info INTEGER
);
insert into test(score, user_id, info)
values
(1000, 1, 1),
(1000, 1, 2),
(2000, 2, 3),
(2001, 2, 1);
--
SELECT t.*
FROM test t
JOIN (SELECT test.user_id, max(score) as mi FROM test GROUP BY user_id) j ON
t.score = j.mi AND
t.user_id = j.user_id
ORDER BY score DESC, info ASC;
Expected output:
+-------+---------+------+
| score | user_id | info |
+-------+---------+------+
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
+-------+---------+------+
--> every user_id is present with the row where the user had the highest score value.
Real output:
+-------+---------+------+
| score | user_id | info |
+-------+---------+------+
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
| 1000 | 1 | 2 |
+-------+---------+------+
--> when there are duplicate values, user show up multiple times.
Anyone who can point me in the right direction?
I assume when there are duplicate scores you want the lowest info just like your expected output.
With NOT EXISTS:
select t.* from test t
where not exists (
select 1 from test
where user_id = t.user_id and (
score > t.score or (score = t.score and info < t.info)
)
);
See the demo.
For MySql 8.0+ you can use ROW_NUMBER():
select t.score, t.user_id, t.info
from (
select *, row_number() over (partition by user_id order by score desc, info asc) rn
from test
) t
where t.rn = 1
See the demo.
Results:
| score | user_id | info |
| ----- | ------- | ---- |
| 1000 | 1 | 1 |
| 2001 | 2 | 1 |
If the combination of (user_id, info) is UNIQUE and NOT NULL (or PRIMARY KEY), then you can use a LIMIT 1 subquery in the WHERE clause:
SELECT t.*
FROM test t
WHERE (t.score, t.info) = (
SELECT t2.score, t2.info
FROM test t2
WHERE t2.user_id = t.user_id
ORDER BY t2.score DESC, t2.info ASC
LIMIT 1
)
ORDER BY t.score DESC, t.info ASC;
The result will be:
| score | user_id | info |
|-------|---------|------|
| 2001 | 2 | 1 |
| 1000 | 1 | 1 |
demo on sqlfiddle
SELECT info FROM test HAVING MAX(score) was used to keep the info field relevant with the row containing the MAX(score).
SELECT MAX(score) score, user_id, (SELECT info FROM test HAVING MAX(score)) AS info FROM test GROUP BY user_id ORDER BY score DESC;
I have a table with 2 columns, the first column is called ID and the second is called TRACKING. The ID column has duplicates, I want to to take all of those duplicates and consolidate them into one row where each value from TRACKING from the duplicate row is placed into a new column within the same row and I no longer have duplicates.
I have tried a few suggested things where all of the values would be concatenated into one column but I want these TRACKING values for the duplicate IDs to be in separate columns. The code below did not do what I intended it to.
SELECT ID, TRACKING =
STUFF((SELECT DISTINCT ', ' + TRACKING
FROM #t b
WHERE b.ID = a.ID
FOR XML PATH('')), 1, 2, '')
FROM #t a
GROUP BY ID
I am looking to take this:
| ID | TRACKING |
-----------------
| 5 | 13t3in3i |
| 5 | g13g13gg |
| 3 | egqegqgq |
| 2 | 14y2y24y |
| 2 | 42yy44yy |
| 5 | 8i535i35 |
And turn it into this:
| ID | TRACKING | TRACKING1 | TRACKING2 |
-----------------
| 5 | 13t3in3i | g13g13gg | 8i535i35 |
| 3 | egqegqgq | | |
| 2 | 14y2y24y | 42yy44yy | |
On (relatively) painful way to do this in MySQL is to use correlated subqueries:
select i.id,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 0
) as tracking_1,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 1
) as tracking_2,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 2
) as tracking_3
from (select distinct id from t
) i;
As bad as this looks, it will probably have surprisingly decent performance with an index on (id, tracking).
By the way, your original code with stuff() would put everything into one column:
select id, group_concat(tracking)
from t
group by id;
with test_tbl as
(
select 5 id, 'goog' tracking,'goog' tracking1
union all
select 5 id, 'goog1','goo'
union all
select 2 , 'yahoo','yah'
union all
select 2, 'yahoo1','ya'
union all
select 3,'azure','azu'
), modified_tbl as
(
select id,array_agg(concat(tracking)) Tracking,array_agg(concat(tracking1)) Tracking1 from test_tbl group by 1
)
select id, tracking[safe_offset(0)] Tracking_1,tracking1[safe_offset(0)] Tracking_2, tracking[safe_offset(1)] Tracking_3,tracking1[safe_offset(1)] Tracking_4 from modified_tbl where array_length(Tracking) > 1
I have a table like : session is the name of the table for example
With columns: Id, sessionDate, user_id
What i need:
Delta should be a new calculated column
Id | sessionDate | user_id | Delta in days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-03-21 00:00:00 | 2 | NULL
3 | 2011-04-22 00:00:00 | 2 | NULL
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-03-21 00:00:00 | 4 | NULL
6 | 2011-04-22 00:00:00 | 4 | NULL
Delta is the Difference between the timestamps
What i want is a result for Delta Timestamp (in Days) for the the previous row and the current row grouped by the user_id.
this should be the result:
Id | sessionDate | user_id | Delta in Days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-02-21 00:00:00 | 2 | 1
3 | 2011-02-22 00:00:00 | 2 | 1
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-02-23 00:00:00 | 4 | 3
6 | 2011-02-25 00:00:00 | 4 | 2
I already have a solution for a specific user_id:
SELECT user_id, sessionDate,
abs(DATEDIFF((SELECT MAX(sessionDate) FROM session WHERE sessionDate < t.sessionDate and user_id = 1), sessionDate)) as Delta_in_days
FROM session AS t
WHERE t.user_id = 1 order by sessionDate asc
But for more user_ids i didnĀ“t find any solution
Hope somebody can help me.
Try this:
drop table a;
create table a( id integer not null primary key, d datetime, user_id integer );
insert into a values (1,now() + interval 0 day, 1 );
insert into a values (2,now() + interval 1 day, 1 );
insert into a values (3,now() + interval 2 day, 1 );
insert into a values (4,now() + interval 0 day, 2 );
insert into a values (5,now() + interval 1 day, 2 );
insert into a values (6,now() + interval 2 day, 2 );
select t1.user_id, t1.d, t2.d, datediff(t2.d,t1.d)
from a t1, a t2
where t1.user_id=t2.user_id
and t2.d = (select min(d) from a t3 where t1.user_id=t3.user_id and t3.d > t1.d)
Which means: join your table to itself on user_ids and adjacent datetime entries and compute the difference.
If id is really sequential (as in your sample data), the following should be quite efficient:
select t.id, t.sessionDate, t.user_id, datediff(t2.sessiondate, t.sessiondate)
from table t left outer join
table tprev
on t.user_id = tprev.user_id and
t.id = tprev.id + 1;
There is also another efficient method using variables. Something like this should work:
select t.id, t.sessionDate, t.user_id, datediff(prevsessiondate, sessiondate)
from (select t.*,
if(#user_id = user_id, #prev, NULL) as prevsessiondate,
#prev := sessiondate,
#user_id := user_id
from table t cross join
(select #user_id := 0, #prev := 0) vars
order by user_id, id
) t;
(There is a small issue with these queries where the variables in the select clause may not be evaluated in the order we expect them to. This is possible to fix, but it complicates the query and this will usually work.)
Although you have choosen an answer here is another way of achieving it
SELECT
t1.Id,
t1.sessionDate,
t1.user_id,
TIMESTAMPDIFF(DAY,t2.sessionDate,t1.sessionDate) as delta
from myTable t1
left join myTable t2
on t1.user_id = t2.user_id
AND t2.Id = (
select max(Id) from myTable t3
where t1.Id > t3.Id AND t1.user_id = t3.user_id
);
DEMO
I have a table as so...
----------------------------------------
| id | name | group | number |
----------------------------------------
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 3 | james | 2 | 2 |
| 4 | steven | 2 | 5 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
----------------------------------------
I'm running a select like so:
SELECT * FROM table WHERE number IN (2,3);
The problem im trying to solve is that I want to only grab get results from groups that have 1 or more rows of each number. For instance the above query is returning id's 1-2-3-5-6, when I'd like the results to exclude id 3 since the group of '2' can only return 1 result for the number of '2' and not for BOTH 2 and 3, since there's no row with the number 3 for the group 2 i'd like it to not even select id 3 at all.
Any help would be great.
Try it this way
SELECT *
FROM table1 t
WHERE number IN(2, 3)
AND EXISTS
(
SELECT *
FROM table1
WHERE number IN(2, 3)
AND `group` = t.`group`
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
)
or
SELECT *
FROM table1 t JOIN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
) q
ON t.`group` = q.`group`;
or
SELECT *
FROM table1
WHERE `group` IN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
);
Sample output (for both queries):
| ID | NAME | GROUP | NUMBER |
|----|-------|-------|--------|
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
Here is SQLFiddle demo
On this, you can approach from a fun way with multiple joins for what you WANT qualified, OR, apply a prequery to get all qualified groups as others have suggested, but readability is a bit off for me..
Anyhow, here's an approach going through the table once, but with joins
select DISTINCT
T.id,
T.Name,
T.Group,
T.Number
from
YourTable T
Join YourTable T2
on T.Group = T2.Group AND T2.Group = 2
Join YourTable T3
on T.Group = T3.Group AND T3.Group = 3
where
T.Number IN ( 2, 3 )
So on the first record, it is pointing to by it's own group to the T2 group AND the T2 group is specifically a 2... Then again, but testing the group for the T3 instance and T3's group is a 3.
If it cant complete the join to either of the T2 or T3 instances, the record is done for consideration, and since indexes work great for joins like this, make sure you have one index for your NUMBER criteria, and another index on the (GROUP, NUMBER) for those comparisons and the next query sample...
If doing by more than this simple 2, but larger group, prequery qualified groups, then join to that
select
YT2.*
from
( select YT1.group
from YourTable YT1
where YT1.Number in (2, 3)
group by YT1.group
having count( DISTINCT YT1.group ) = 2 ) PreQualified
JOIN YourTable YT2
on PreQualified.group = YT2.group
AND YT2.Number in (2,3)
Maybe this,if I understand you
SELECT id FROM table WHERE `group` IN
(SELECT `group` FROM table WHERE number IN (2,3)
GROUP BY `group`
HAVING COUNT(DISTINCT number)=2)
SQL Fiddle
This will return all ids where BOTH numbers exist in a group.Remove DISTINCT if you want ids for groups where just one numbers is in.