I need this sql query in a function in my controller in ruby on rails 2.3.
SELECT name, created_at AS "date" FROM table1 WHERE created_at = '2015-07-15' ORDER by created_at;
This is because I have two more queries before this one, and they have a field named 'date', and I need to concatenate every one and sort them by the field named 'date', and just this last one table don't have a field named 'date'.
Ok, I found this solution that helps me, for another person maybe need it..
table1.find_by_sql["SELECT name, created_at AS 'date' FROM table1 WHERE created_at = '2015-07-15' ORDER by created_at"]
Then I can sort all my query results (including the previous tables) by the field 'date'..
Related
I am working with a table in SQLite3 on Python, and the table has four attributes -
ID, added(date when course added to subsection), course_id, course_subsection_title
Of these, the date_added attribute contains the date the course was added to the course_subsection, the course_id contains the ID of the course added, and the ID contains the ID of the course_subsection. The query I have to write groups the courses by the course_subsections, and then calculates the number of days passed between the oldest course added and the most recent course added to the particular course_subsection. I have a query for it as follows, but it appears to be working incorrectly -
query = '''
SELECT
course_subsections.ID as id,
CAST((JulianDay(max(course_subsections.added)) - JulianDay(min(course_subsections.added))) as INTEGER) as num_days_passed
FROM course_subsections
WHERE course_subsections.ID = (
SELECT course_subsections.ID
FROM course_subsections
GROUP BY course_subsections.ID
)
ORDER BY num_days_passed DESC
'''
This isn't working how it should, however. I am very new to SQLite, and still have some confusion about how nested subqueries work in general. Can you help me out with where I am going wrong here?
Remove the WHERE clause.
It filters out all the IDs but one of the table:
SELECT ID,
CAST(JulianDay(MAX(added)) - JulianDay(MIN(added)) AS INTEGER) AS num_days_passed
FROM course_subsections
GROUP BY ID
ORDER BY num_days_passed DESC;
See a simplified demo.
I have some problems with the new version of SQL, which uses the only_full_group_by option. I have two tables: sensor and data. The sensor table HAS_MANY data, so the data table has a foreignkey to the pk of sensor table. Here is one relation:
'avg' => array(self::HAS_MANY, 'Data', 'sensor_id', 'select' => 'AVG(value) AS avg, date AS date', 'group' => 'date', 'order' => 'date desc')
Yii is complaining because there is a column in the SELECT statement that is not aggrgated. This column is the pk of the data table, which seems to be automatically added in the select of the generated query, in fact the query is:
SELECT AVG(value) AS avg, date as date, id_data FROM `data` `avg` WHERE ... GROUP BY date ORDER ...
What I want to do is to remove the added pk or wrap it with ANY_VALUE, so that the only_full_group_by option does not complain anymore.
Thank you
Found the answer by myself, writing it here to make it available to everyone.
To solve the problem you just need to add the id in the SELECT clause and give it the same alias given by the yii generate query. So, in my query I have
SELECT id as t0_c1 ...
I changed it to
SELECT ANY_VALUE(id) as t0_c1 ...
This is a more to the point follow-up of my other question:
How does DISTINCT interact with ORDER BY?
Given a table:
CREATE TABLE events (
order TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
value INT NOT NULL
);
At every insertion, value will be taken from a finite set.
How do I get the order all the elements of my set were last inserted into the database? I thought of doing SELECT DISTINCT value FROM event ORDER BY "order" DESC;, but according to answer in my other question, this won't work.
Answer from your other post.
Using max and grouping on the user you get the most recent timestamp by user.
SELECT
MAX([order]) AS MaxOrd
, value
FROM Event
GROUP BY value
ORDER BY MaxOrd DESC
Firstly I'm assuming you mean where instead of were.
So if you're looking to order by insertion date descending, then it would be just as you have:
SELECT DISTINCT value FROM event ORDER BY "order" DESC;
I don't know why that wouldn't work. I tested it on one of my tables containing a timestamp and it works fine.
I have Articles that have_many Metrics. I wish to order the Articles by a specific Metric.value when Metric.name = "score". (Metric records various article stats as 'name' and 'value' pairs. An Article can have multiple metrics, and even multiple 'scores', although I'm only interested in ordering by the most recent.)
class Article
has_many :metrics
class Metric
# name :string(255)
# value :decimal(, )
belongs_to :article
I'm struggling to write a scope to do this - any ideas? Something like this?
scope :highest_score, joins(:metrics).order('metrics.value DESC')
.where('metrics.name = "score"')
UPDATE:
An article may have many "scores" stored in the metrics table (as they are calculated weekly/monthly/yearly etc.) but I'm only interested in using the first-found (most recent) "score" for any one article. The Metric model has a default_scope that ensures DESCending ordering.
Fixed typo on quote location for 'metrics.value DESC'.
Talking to my phone-a-friend uber rails hacker, it looks likely I need a raw SQL query for this. Now I'm in way over my head... (I'm using Postgres if that helps.)
Thanks!
UPDATE 2:
Thanks to Erwin's great SQL query suggestion I have a raw SQL query that works:
SELECT a.*
FROM articles a
LEFT JOIN (
SELECT DISTINCT ON (article_id)
article_id, value
FROM metrics m
WHERE name = 'score'
ORDER BY article_id, date_created DESC
) m ON m.article_id = a.id
ORDER BY m.value DESC;
article_list_by_desc_score = ActiveRecord::Base.connection.execute(sql)
Which gives an array of hashes representing article data (but not article objects??).
Follow-up question:
Any way of translating this back into an activerecord query for Rails? (so I can then use it in a scope)
SOLUTION UPDATE:
In case anyone is looking for the final ActiveRecord query - many thanks to Mattherick who helped me in this question. The final working query is:
scope :highest_score, joins(:metrics).where("metrics.name"
=> "score").order("metrics.value desc").group("metrics.article_id",
"articles.id", "metrics.value", "metrics.date_created")
.order("metrics.date_created desc")
Thanks everyone!
The query could work like this:
SELECT a.*
FROM article a
LEFT JOIN (
SELECT DISTINCT ON (article_id)
article_id, value
FROM metrics m
WHERE name = 'score'
ORDER BY article_id, date_created DESC
) m ON m.metrics_id = a.metrics_id
ORDER BY m.value DESC;
First, retrieve the "most recent" value for name = 'score' per article in the subquery m. More explanation for the used technique in this related answer:
Select first row in each GROUP BY group?
You seem to fall victim to a very basic misconception though:
but I'm only interested in using the first-found (most recent) "score"
for any one article. The Metric model has a default_scope that ensures DESCending ordering.
There is no "natural order" in a table. In a SELECT, you need to ORDER BY well defined criteria. For the purpose of this query I am assuming a column metrics.date_created. If you have nothing of the sort, you have no way to define "most recent" and are forced to fall back to an arbitrary pick from multiple qualifying rows:
ORDER BY article_id
This is not reliable. Postgres will pick a row as it choses. May change with any update to the table or any change in the query plan.
Next, LEFT JOIN to the the table article and ORDER BY value. NULL sorts last, so articles without qualifying value go last.
Note: some not-so-smart ORMs (and I am afraid Ruby's ActiveRecord is one of them) use the non-descriptive and non-distinctive id as name for the primary key. You'll have to adapt to your actual column names, which you didn't provide.
Performance
Should be decent. This is a "simple" query as far as Postgres is concerned. A partial multicolumn index on table metrics would make it faster:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created)
WHERE name = 'score';
Columns in this order. In PostgreSQL 9.2+ you could add the column value to make index-only scans possible:
CREATE INDEX metrics_some_name_idx ON metrics(article_id, date_created, value)
WHERE name = 'score';
I am trying to write a query in Postgresql that pulls a set of ordered data and filters it by a distinct field. I also need to pull several other fields from the same table row, but they need to be left out of the distinct evaluation. example:
SELECT DISTINCT(user_id) user_id,
created_at
FROM creations
ORDER BY created_at
LIMIT 20
I need the user_id to be DISTINCT, but don't care whether the created_at date is unique or not. Because the created_at date is being included in the evaluation, I am getting duplicate user_id in my result set.
Also, the data must be ordered by the date, so using DISTINCT ON is not an option here. It required that the DISTINCT ON field be the first field in the ORDER BY clause and that does not deliver the results that I seek.
How do I properly use the DISTINCT clause but limit its scope to only one field while still selecting other fields?
As you've discovered, standard SQL treats DISTINCT as applying to the whole select-list, not just one column or a few columns. The reason for this is that it's ambiguous what value to put in the columns you exclude from the DISTINCT. For the same reason, standard SQL doesn't allow you to have ambiguous columns in a query with GROUP BY.
But PostgreSQL has a nonstandard extension to SQL to allow for what you're asking: DISTINCT ON (expr).
SELECT DISTINCT ON (user_id) user_id, created_at
FROM creations
ORDER BY user_id, created_at
LIMIT 20
You have to include the distinct expression(s) as the leftmost part of your ORDER BY clause.
See the manual on DISTINCT Clause for more information.
If you want the most recent created_at for each user then I suggest you aggregate like this:
SELECT user_id, MAX(created_at)
FROM creations
WHERE ....
GROUP BY user_id
ORDER BY created_at DESC
This will return the most recent created_at for each user_id
If you only want the top 20, then append
LIMIT 20
EDIT: This is basically the same thing Unreason said above... define from which row you want the data by aggregation.
The GROUP BY should ensure distinct values of the grouped columns, this might give you what you are after.
(Note I'm putting in my 2 cents even though I am not familiar with PostgreSQL, but rather MySQL and Oracle)
In MySql
SELECT user_id, created_at
FROM creations
GROUP BY user_id
ORDER BY user_id
In Oracle sqlplus
SELECT user_id, FIRST(created_at)
FROM creations
GROUP BY user_id
ORDER BY user_id
These will give you the user_id followed by the first created_at associated with that user_id. If you want a different created_at you have the option to substitute FIRST with other functions like AVG, MIN, MAX, or LAST in Oracle, you can also try adding ORDER BY on other columns (including ones that are not returned, to give you a different created_at.
Your question is not well defined - when you say you need also other data from the same row you are not defining which row.
You do say you need to order the results by created_at, so I will assume that you want values from the row with min created_at (earliest).
This now becomes one of the most common so SQL questions - retrieving rows containing some aggregate value (MIN, MAX).
For example
SELECT user_id, MIN(created_at) AS created_at
FROM creations
GROUP BY user_id
ORDER BY MIN(create_at)
LIMIT 20
This approach will not let you (easily) pick other values from the same row.
One approach that will let you pick other values is
SELECT c.user_id, c.created_at, c.other_columns
FROM creations c LEFT JOIN creation c_help
ON c.user_id = c_help.user_id AND c.created_at > c_help.create_at
WHERE c_help IS NULL
ORDER BY c.created_at
LIMIT 20
Using a sub-query was suggested by someone on the irc #postgresql channel. It worked:
SELECT user_id
FROM (SELECT DISTINCT ON (user_id) * FROM creations) ss
ORDER BY created_at DESC
LIMIT 20;