Select rows who's FIRST result is a specific value - mysql

I've got a custom written analytics system running and I'm trying to write a query that returns users who landed on a specific page as their first hit. The relevant parts of the table is setup as such, with some simple data:
pageviews Table
+----+---------------------+----------+-------------+
| id | time_in | users_id | articles_id |
+----+---------------------+----------+-------------+
| 0 | 2013-08-15 00:00:00 | 0 | 0 |
| 1 | 2013-08-16 00:00:00 | 0 | 1 |
| 2 | 2013-08-17 00:00:00 | 1 | 1 |
| 3 | 2013-08-18 00:00:00 | 1 | 0 |
| 4 | 2013-08-19 00:00:00 | 1 | 1 |
| 5 | 2013-08-20 00:00:00 | 2 | 1 |
+----+---------------------+----------+-------------+
NOTE: The ID fields in my DB are actually using GUIDs, not ints as in this simple example.
Now, if I want to see who read article 1 as their first hit, I want my query to return users 1 and 2, but not 0, as user 0 saw article 0 as their first hit on the site. Conversely, if I want to see who read article 0 first, the query would only return user 0.
Here is my query thus far:
SELECT
*
FROM
pageviews
WHERE
articles_id = 1
GROUP BY
users_id
ORDER BY
time_in
But this returns distinct user IDs for all users who've read article 1, not filtering out the users who did not see it as their first result. I feel like I'm going the wrong direction with my query, so I'm turning towards you guys.
Thanks in advance.

One way to do it
SELECT v.users_id
FROM pageviews v JOIN
(
SELECT users_id, MIN(time_in) time_in
FROM pageviews
GROUP BY users_id
) q ON v.users_id = q.users_id AND v.time_in = q.time_in
WHERE v.articles_id = 1
Output:
| USERS_ID |
------------
| 1 |
| 2 |
Here is SQLFiddle demo

Another way:
SELECT users_id
FROM pageviews p
WHERE articles_id = 1
AND time_in = (SELECT MIN(time_in) from pageviews p2 WHERE p2.users_id = p.users_id)
SQLFiddle here

Related

How to count users with where condition previous row

please help me i have no idea for this...
I have table like this (create_at YYYY-MM-DD). ID is auto increment
-----------------------------------------------------------------
| ID | id_user | activity | create_at |
-----------------------------------------------------------------
| 1 | 10 | A | 2017-10-11 |
| 2 | 52 | A | 2017-10-11 |
| 3 | 41 | A | 2017-10-12 |
| 4 | 52 | A | 2017-10-12 |
| 5 | 41 | B | 2017-10-12 |
| 6 | 52 | B | 2017-10-13 |
| 7 | 10 | B | 2017-10-14 |
-----------------------------------------------------------------
How to get count (mysql) user who doing activity "B" after activity "A" in sameday create_at.. In this case, the result is 1 (IDUser 41).. How can i do this in mysql? thankyou
We could use a semi-join or a correlated subquery.
we start like this, users that are doing activity B
SELECT t.id_user
FROM table_like_this t
WHERE t.activity = 'B'
we can match those rows to users that are doing activity A on the "same day" with JOIN operation back to the same table...
SELECT t.id_user
FROM table_like_this t
JOIN table_like_this r
ON r.id_user = t.id_user
AND r.create_at = t.create_at
AND r.activity = 'A'
WHERE t.activity = 'B'
As far as whether activity B is occurring "after" activity A, I don't see any information in the table that can tell us that (we can't tell what time each activity A and B occurred, and can't determine which one was "after" the other.)
For testing, we can include other columns in the SELECT list, to verify which rows from t and r are being returned, if the matching is being done properly.
Once we are satisfied, we can replace the SELECT list, to get a count of distinct id_user
SELECT COUNT(DISTINCT t.id_user)
FROM ...
Note that this will collapse occurrences of id_user that performed activity A and B on several different days so that the id_user will be counted only once.
If we want to count the number of days for each id_user, and include each of those days in the count, the query would need to be changed.

mysql Quiz leaderboard filter by points, time taken

I have a quiz report table which shows a report for every quiz a user takes. I need to create a leaderboard from this, which shows the top users best score, filtering by points and then time taken.
here is a link to a sql fiddle http://sqlfiddle.com/#!2/65fbf0/1
I am really struggling as i need to filter the results by two columns for one user, my ideal result would be
Results for Quiz id 1
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,15 2015| 1 |
| 3 | 2 | 0.5 | May,15 2015| 1 |
Then a separate query for all quiz's showing the results from the last week
Results from all Quizzs
---------------------------------------------------------------
| user_id | points | time_spend | start_dt | quiz_id |
| 1 | 3 | 0.5 | May,15 2015| 1 |
| 2 | 3 | 0.8 | May,13 2015| 3 |
| 3 | 2 | 0.5 | May,12 2015| 2 |
You can sort on multiple columns like this:
select *
from QuizReport
where quiz_id = 1
order by points desc, time_spend asc;
select *
from (
select *
from QuizReport
where start_dt >= subdate(curdate(), 7)
order by points desc, time_spend asc) a
group by user_id;
group_by user_id preserves the first row for every user_id. since the inner query sorts rows by score, the outer query will display best row for every user.

Count rows with specific value over multiple rows

Its very hard for to set a proper title, because I dont know how I describe my problem.
I have a table like this:
dlID | dl_seID | dlEpisode | dlFlag
___________________________________
1 | 1 | 1 | 0
2 | 1 | 2 | 1
3 | 1 | 3 | 1
4 | 2 | 1 | 1
5 | 2 | 2 | 0
6 | 3 | 1 | 0
What i want is a select query where I get something like this:
dlID | dl_seID | dlEpisode | dlFlag | dlFlagCount
_________________________________________________
1 | 1 | 1 | 0 | 2
2 | 1 | 2 | 1 | 2
3 | 1 | 3 | 1 | 2
4 | 2 | 1 | 1 | 1
5 | 2 | 2 | 0 | 1
6 | 3 | 1 | 0 | 0
dlFlagCount shoud be a counter of dlFlag = 1 where dl_seID = dl_seID.
Second try:
I need a value where I see how many Flags have the value 1 with the same dl_seID.
Is that possible?
I hope you guys know what I want^^
Regards
Try this:
select
a.*,
ifnull(b.ctflags,0)
from
tablea a left join
( select dl_seID, count(dlFlag) ctflags
from tablea
where dlFlag=1
group by dl_seID ) b on (a.dl_seID = b.dl_seID)
The left join is just to get the registry with 0 flags
See the fiddle: http://sqlfiddle.com/#!2/ef9b0/5
EDIT:
As op requested some explanation, here it goes:
What you asked is to count the amount of flags by the dl_seID and to do that you need to do this you separeta your problems, first you get the count for the dl_seID by flags, this is this subquery:
select dl_seID, count(dlFlag) ctflags
from tablea
where dlFlag=1
group by dl_seID
This became a 'separe table' or a new group of data, whatever you wanna call it. Then you have to join this with your original data (from your table) like the query for answer.
The left join part is because maybe there are some data that wont complain with where dlFlag=1 therefore if you want to get then as 0 you have to bring all values from table that exists or not on our created subgroup. And this ifnull(b.ctflags,0) is for theese data data exists on your table but has no flags (for your problem). If you use just b.ctflags it will bring null.
SELECT x.*
, COALESCE(y.flagcount,0) flagcount
FROM my_table x
LEFT
JOIN
( SELECT seID
, COUNT(*) flagcount
FROM my_table
WHERE flag = 1
GROUP
BY seid
) y
ON y.seid = x.seid;

Create a view to add a row no. for a specific set of data filtered by a column index

I have a database table like the one mentioned below.
id | lecture | subject_id | date | is_deleted
------|--------------------|------------|------------|-----------
1 | Introduction | 1 | 2012-08-10 | 0
2 | Structure | 2 | 2012-08-15 | 1
3 | Introduction | 2 | 2012-08-12 | 0
4 | Functions | 1 | 2012-08-14 | 1
5 | Material | 2 | 2012-08-18 | 0
6 | Requirements | 1 | 2012-08-16 | 0
7 | Analysis | 1 | 2012-08-11 | 0
I need to make a view out of this table (Lecture), which will display a row no. (flow no.) for each subject ordered by date, removing is_deleted = 1 rows. Simply, making a flow no. for each lecture in a particular subject ordered by date only with not deleted lectures. So, the view made by above data will look like the following.
flow_no | id | date | lecture | subject_id
--------|------|------------|--------------------|------------
1 | 1 | 2012-08-10 | Introduction | 1
2 | 7 | 2012-08-11 | Analysis | 1
3 | 6 | 2012-08-16 | Requirements | 1
1 | 3 | 2012-08-12 | Introduction | 2
2 | 5 | 2012-08-18 | Material | 2
I tried to do this in several ways and everything failed. It's highly appreciated if someone could help me to resolve this. (mysql)
SELECT #rownum := #rownum + 1 flow_no, id, date, lecture, subject_id
FROM subjects, (SELECT #rownum := 0) r
WHERE is_deleted = 0
ORDER BY subject_id, date, id
I'll leave it to you to turn that into a view.
To restart the flow_no per subject you have a few choices:
Copy the query above, and wrap it as a subquery, then calculate the min(flow_no) grouping by subject_id, the join it to the above query, and subtract the min flow_no from each row.
You could assign the subject_id to a variable, then check the variable against the current subject_id and reset the rownum variable each time - I'm not even sure this is possible.
You could write a stored procedure to do this - get a list of unique subject_id, then run a bunch of queries for each one and output them.
None of these options sound appealing to me. If this was me I would abandon doing this in a query.
I found the answer by the support of another online forum's member. The following will give the exact result required. If there is any same Lecture.date rows it generates flow_no in Lecture.id order for those rows.
SELECT id, lecture, Lecture.subject_id, date, c,
(SELECT COUNT(subject_id) + 1 FROM Lecture AS l
WHERE l.subject_id = Lecture.subject_id
AND (l.date < Lecture.date OR (l.date = Lecture.date AND l.id < Lecture.id))
AND is_deleted != 1
) AS flow_no
FROM Lecture
INNER JOIN
(
SELECT subject_id, COUNT(subject_id) AS c
FROM Lecture
WHERE is_deleted != 1
GROUP BY subject_id
) AS counts
ON Lecture.subject_id = counts.subject_id
WHERE is_deleted != 1
ORDER BY subject_id, date;

Joining one table to the latest row in another table using MySQL

I want to join two tables in a special way, first table is devices which has a list of devices.
The second table is datalog which is where abit of data is stored for everytime a device in devices gets polled.
Devices Table:
+----------+------------+----------------------------+---------------------+
| deviceId | deviceName | deviceDescription | timeCreated |
+----------+------------+----------------------------+---------------------+
| 1 | System 1 | Main System in Server Room | 2010-01-01 00:00:00 |
| 2 | System 2 | Outdoor System | 2010-01-01 00:00:00 |
+----------+------------+----------------------------+---------------------+
DataLog Table:
+----+---------------------+----------+-----------+---------+
| id | time_stamp | DeviceId | FuelLevel | Voltage |
+----+---------------------+----------+-----------+---------+
| 1 | 2010-01-01 00:00:00 | 1 | 60 | 220 |
| 2 | 2010-01-01 00:00:00 | 2 | 20 | 221 |
| 3 | 2010-01-02 00:00:00 | 1 | 100 | 219 |
| 4 | 2010-01-02 00:00:00 | 2 | 100 | 222 |
| 5 | 2010-01-03 00:00:00 | 1 | 80 | 219 |
| 6 | 2010-01-03 00:00:00 | 2 | 99 | 220 |
+----+---------------------+----------+-----------+---------+
Currently I am getting the latest data for each device using a query on the DataLog table with:
Where DeviceId = 1 ORDER BY timestamp DESC LIMIT 1
What I would like is one query to return a list of all devices, with the columns joined with the latest data for each device like this:
+----------+------------+----------------------------+---------------------+-----------+---------+
| deviceId | deviceName | deviceDescription | time_stamp |FuelLevel | Voltage |
+----------+------------+----------------------------+---------------------+-----------+---------+
| 1 | System 1 | Main System in Server Room | 2010-01-03 00:00:00 | 80 | 219 |
| 2 | System 2 | Outdoor System | 2010-01-03 00:00:00 | 99 | 220 |
+----------+------------+----------------------------+---------------------+-----------+---------+
You can't do the "limit 1" at the outer level, you loose what you are looking for... ALL devices last entry. Use a pre-query for the last ID of each device, then join back...
select
Devices.*,
DataLog.Time_Stamp,
DataLog.FuelLevel,
DataLog.Voltage
from
( select DeviceID,
max( ID ) LastActionID
from
DataLog
group by
1 ) LastInstance
join DataLog
on LastInstance.LastActionID = DataLog.ID
join Devices
on LastInstance.DeviceID = Devices.DeviceID
order by
Devices.DeviceName
Per your last comment, I would actually change to something like...
Update your device table with a "LastLogID". Then, via a trigger an insert into your DataLog table, update the Device table immediately with that new ID... This way, you never need to pre-query the data log directly.. You'll already HAVE the last ID and run from that directly to the data log joined by that ID.
I know it's horrible, not elegant and time consuming, but this query works:
SELECT deviceId,deviceName,deviceDescription,
(SELECT time_stamp FROM datalog
WHERE datalog.DeviceId=devices.deviceId
ORDER BY time_stamp DESC LIMIT 0,1) time_stamp,
(SELECT FuelLevel FROM datalog
WHERE datalog.DeviceId=devices.deviceId
ORDER BY time_stamp DESC LIMIT 0,1) FuelLevel,
(SELECT Voltage FROM datalog
WHERE datalog.DeviceId=devices.deviceId
ORDER BY time_stamp DESC LIMIT 0,1) Voltage
FROM devices
I tried to have a single subquery retrieving multiple columns, but MySql complains because it wants only one column.
try
by the way if u want only latest row then u can search it by auto increment field (datalog_table.id)
SELECT dvc.deviceId,dvc.deviceName,dvc.deviceDescription,
dtl.time_stamp,dtl.FuelLevel,dtl.Voltage
FROM device_table dvc
INNER JOIN datalog_table dtl
ON dtl.DeviceId=dvc.deviceId
ORDER BY dtl.id LIMIT 1
SELECT
d.deviceId, d.deviceName, d.deviceDescription,
dl.time_stamp, dl.FuelLevel, dl.Voltage
FROM Device d, DataLog dl
WHERE d.deviceId=dl.deviceID
ORDER BY time_stamp DESC
LIMIT 1