DISTINCT ON query w/ ORDER BY max value of a column - mysql

I've been tasked with converting a Rails app from MySQL to Postgres asap and ran into a small issue.
The active record query:
current_user.profile_visits.limit(6).order("created_at DESC").where("created_at > ? AND visitor_id <> ?", 2.months.ago, current_user.id).distinct
Produces the SQL:
SELECT visitor_id, MAX(created_at) as created_at, distinct on (visitor_id) *
FROM "profile_visits"
WHERE "profile_visits"."social_user_id" = 21
AND (created_at > '2015-02-01 17:17:01.826897' AND visitor_id <> 21)
ORDER BY created_at DESC, id DESC
LIMIT 6
I'm pretty confident when working with MySQL but I'm honestly new to Postgres. I think this query is failing for multiple reasons.
I believe the distinct on needs to be first.
I don't know how to order by the results of max function
Can I even use the max function like this?
The high level goal of this query is to return the 6 most recent profile views of a user. Any pointers on how to fix this ActiveRecord query (or it's resulting SQL) would be greatly appreciated.

The high level goal of this query is to return the 6 most recent
profile views of a user.
That would be simple. You don't need max() nor DISTINCT for this:
SELECT *
FROM profile_visits
WHERE social_user_id = 21
AND created_at > (now() - interval '2 months')
AND visitor_id <> 21 -- ??
ORDER BY created_at DESC NULLS LAST, id DESC NULLS LAST
LIMIT 6;
I suspect your question is incomplete. If you want:
the 6 latest visitors with their latest visit to the page
then you need a subquery. You cannot get this sort order in one query level, neither with DISTINCT ON, nor with window functions:
SELECT *
FROM (
SELECT DISTINCT ON (visitor_id) *
FROM profile_visits
WHERE social_user_id = 21
AND created_at > (now() - interval '2 months')
AND visitor_id <> 21 -- ??
ORDER BY visitor_id, created_at DESC NULLS LAST, id DESC NULLS LAST
) sub
ORDER BY created_at DESC NULLS LAST, id DESC NULLS LAST
LIMIT 6;
The subquery sub gets the latest visit per user (but not older than two months and not for a certain visitor21. ORDER BY must have the same leading columns as DISTINCT ON.
You need the outer query to get the 6 latest visitors then.
Consider the sequence of events:
Best way to get result count before LIMIT was applied
Why NULLS LAST? To be sure, you did not provide the table definition.
PostgreSQL sort by datetime asc, null first?

Related

How to get the last rows per group in mysql

I have a query that retrieves the reservation made by a team
the query computes and retrieves good but the problem is that I only want to retrieve the latest reservation made by the team but my query shows their first reservation made.
Here is the complete query
select
tbl_lab_reservations.id,
tbl_lab_reservations.full_desc,
serial_number,
rsvn_owner,
reservation_id,
reservation_date_end,
reservation_date_start,
(SELECT DATEDIFF( if(reservation_date_end = '0000-00-00', CURDATE(), reservation_date_end),
reservation_date_start)+1) as totalNumberOfDaysReserve
from tbl_lab_reservations
join tbl_lab_assets on tbl_lab_assets.id = tbl_lab_reservations.lab_id
where tbl_lab_reservations.full_desc = 'Dell Optiplex 380'
and tbl_lab_reservations.asset_status = 'Idle'
group by serial_number, rsvn_owner
ORDER BY tbl_lab_reservations.id ASC
The query that you have given is correct for showing the records from first to latest as the order by clause is asc. To retrieve the latest record,
change the order by clause to desc
from which you will get the latest record as the first one in the result(only if you have the tbl_lab_reservations.id is unique for all records).
To get only the latest record and omit the other, you have the limit keyword. The limit should be used at the end of the query to set the limit of records to be fetched.
Syntax : limit N ,where N specifies the number of record.
Example for you.
link
select id, events from msql
where id < '05'
group by id
order by id desc
limit 1

Top-10 mysql query

I'm in need of a better way of retrieving top 10 distinct UID from some tables I have.
The setup:
Table user_view_tracker
Contains pairs of {user id (uid), timestamp (ts)}
Is growing every day (today it's 41k entries)
My goal:
To produce a top 10 of most viewed user id's in the table user_view_tracker
My current code is working, but killing the database slowly:
select
distinct uvt.uid as UID,
(select count(*) from user_view_tracker temp where temp.uid=uvt.uid and temp.ts>date_sub(now(),interval 1 month)) as CLICK
from user_view_tracker uvt
order by CLICK
limit 10
It's quite obvious that a different data structure would help. But I can't do that as of now.
First of all, delete that subquery, this should be enough ;)
select
uvt.uid as UID
,count(*) as CLICK
from
user_view_tracker uvt
where
uvt.ts > date_sub(now(),interval 1 month)
group by
uvt.uid
order by CLICK DESC
limit 10
Try:
select uid, count(*) as num_stamps
from user_view_tracker
where ts > date_sub(now(), interval 1 month)
group by uid
order by 2 desc limit 10
I kept your criteria as far as getting the count for just the past month. You can remove that line if you want to count all.
The removal of DISTINCT should improve performance. It is not necessary if you aggregate in your outer query and group by uid, as that will aggregate the data to one row per uid with the count.
You should use Aggregate functions in MySQL
SELECT UID, COUNT(ts) as Number_Of_Views FROM user_view_tracker
GROUP BY UID
ORDER BY Number_Of_Views DESC
LIMIT 10
A simple demo which selects the top 10 UID viewed
http://sqlfiddle.com/#!2/907c10/3

Need to improve sql performance

Table temporary_search_table
post_id,property_status, property_address,....more 30 field
Table search_meta
meta_id,search_id,status,created_date
Ok I need Total data which created_date is yesterday. For each temporary_search_table data there may multiple entry within search_meta. So we need to pick last one field from search_meta and check created date is yesterday and property_status is pending. if yes then we can count the number. If there is no data available in search_meta for entry in temporary_search_table then we dont need to count that row within our results.
Here i am attaching my sql data. its work but for 30000 row it take lots of time.
SELECT COUNT(id) FROM temporary_search_table
WHERE property_status = 'pending' AND (1 = (SELECT DATEDIFF(NOW(), created_date)
FROM search_meta WHERE post_id = search_id ORDER BY created_date DESC LIMIT 0,1 ))
Thanks in advance.
Apart from checking the indexes on your table, it would probably be better to not use a correlated sub query and use a straight join instead.
SELECT COUNT(id)
FROM temporary_search_table
INNER JOIN search_meta ON post_id = search_id
WHERE property_status = 'pending' AND DATEDIFF(NOW(), created_date) = 1
ORDER BY created_date DESC
LIMIT 1

Sql Query to count same date entries

All I want to count entries based on date.(i.e entries with same date.)
My table is
You can see 5th and 6th entry have same date.
Now, the real problem as i think is the same date entry have different time so i am not getting what I want.
I am using this sql
SELECT COUNT( created_at ) AS entries, created_at
FROM wp_frm_items
WHERE user_id =1
GROUP BY created_at
LIMIT 0 , 30
What I am getting is this.
I want entries as 2 for date 2012-02-22
The reason you get what you get is because you also compare the time, down to a second apart. So any entries created the same second will be grouped together.
To achieve what you actually want, you need to apply a date function to the created_at column:
SELECT COUNT(1) AS entries, DATE(created_at) as date
FROM wp_frm_items
WHERE user_id =1
GROUP BY DATE(created_at)
LIMIT 0 , 30
This would remove the time part from the column field, and so group together any entries created on the same day. You could take this further by removing the day part to group entries created on the same month of the same year etc.
To restrict the query to entries created in the current month, you add a WHERE-clause to the query to only select entries that satisfy that condition. Here's an example:
SELECT COUNT(1) AS entries, DATE(created_at) as date
FROM wp_frm_items
WHERE user_id = 1
AND created_at >= DATE_FORMAT(CURDATE(),'%Y-%m-01')
GROUP BY DATE(created_at)
Note: The COUNT(1)-part of the query simply means Count each row, and you could just as well have written COUNT(*), COUNT(id) or any other field. Historically, the most efficient approach was to count the primary key, since that is always available in whatever index the query engine could utilize. COUNT(*) used to have to leave the index and retrieve the corresponding row in the table, which was sometimes inefficient. In more modern query planners this is probably no longer the case. COUNT(1) is another variant of this that didn't force the query planner to retrieve the rows from the table.
Edit: The query to group by month can be created in a number of different ways. Here is an example:
SELECT COUNT(1) AS entries, DATE_FORMAT(created_at,'%Y-%c') as month
FROM wp_frm_items
WHERE user_id =1
GROUP BY DATE_FORMAT(created_at,'%Y-%c')
You must eliminate the time with GROUP BY
SELECT COUNT(*) AS entries, created_at
FROM wp_frm_items
WHERE user_id =1
GROUP BY DATE(created_at)
LIMIT 0 , 30
Oops, misread it.
Use GROUP BY DATE(created_at)
Try:
SELECT COUNT( created_at ) AS entries, created_at
FROM wp_frm_items
WHERE user_id =1
GROUP BY DATE(created_at)
LIMIT 0 , 30

Find date stored in MySQL row, find all entries after that date in one query

I have a table that stores actions for rate-limiting purposes. What I want to do is fetch the newest row that has a 'key_action' (the action that starts the time for rate-limiting) and then find all entries after that date.
The only way I can currently think to do it is with two queries:
SELECT created_at FROM actions WHERE key_action=1 ORDER BY created_at DESC LIMIT 1
SELECT * FROM actions WHERE created_at >= (created_at from query 1)
Is there a was to combine these two queries into one?
You can make query 1 a subquery of query 2.
SELECT *
FROM actions
WHERE created_at >= (SELECT MAX(created_at)
FROM actions
WHERE key_action=1)
I'd have thought #Joe Stefanelli's answer was right, but Limits are not allowed in subqueries in WHERE statement. From this workaround, I put together this query (not tested)
SELECT * FROM actions
JOIN (SELECT created_at FROM actions WHERE key_action=1 ORDER BY created_at DESC LIMIT 1) createdActions
WHERE actions.created_at >= createdActions.created_at