I want to show posts that are tagged with the current locale first. I thought that the MySQL FIELD function would be a nice way to solve this.
However,
Post.includes(:tags).order("FIELD(tags.name, '#{I18n.locale}') DESC").first
results in
Mysql2::Error: Unknown column 'tags.name' in 'order clause': SELECT spree_posts`.* FROM 'spree_posts' ORDER BY FIELD(tags.name, 'en') DESC LIMIT 1
If I try
Post.includes(:tags).order("FIELD(tags.name, '#{I18n.locale}') DESC").where.not(tags: { name: 'WTF?' } )
it returns the posts according to the FIELD method correctly, but does a join with tags, hence not returning all posts without tags :(
I think this might be a bug in ActiveRecord? It's definitely not the behaviour I expect.
If you want to use includes and ensure Rails loads the table in the main query, you should use references:
Post.includes(:tags).references(:tags).
order("FIELD(tags.name, '#{I18n.locale}') DESC").first
SELECT spree_posts`.* FROM 'spree_posts' ORDER BY FIELD(tags.name,
'en') DESC LIMIT 1
There is no tags table in this query, the table is not loaded, because includes is a smart method, that generates JOIN only when included table is used in WHERE clause.
You need to explicitly define LEFT JOIN:
Post.joins('LEFT JOIN tags ON tags.post_id = post.id').order("FIELD(tags.name, '#{I18n.locale}') DESC").first
(by default joins generates INNER JOIN)
Related
Tag.joins(:quote_tags).group('quote_tags.tag_id').order('count desc').select('count(tags.id) AS count, tags.id, tags.name')
Build query:
SELECT count(tags.id) AS count, tags.id, tags.name FROM `tags` INNER JOIN `quote_tags` ON `quote_tags`.`tag_id` = `tags`.`id` GROUP BY quote_tags.tag_id ORDER BY count desc
Result:
[#<Tag id: 401, name: "different">, ... , #<Tag id: 4, name: "family">]
It not return count column for me. How can I get it?
Have you tried calling the count method on one of the returned Tag objects? Just because inspect doesn't mention the count doesn't mean that it isn't there. The inspect output:
[#<Tag id: 401, name: "different">, ... , #<Tag id: 4, name: "family">]
will only include things that the Tag class knows about and Tag will only know about the columns in the tags table: you only have id and name in the table so that's all you see.
If you do this:
tags = Tag.joins(:quote_tags).group('quote_tags.tag_id').order('count desc').select('count(tags.id) AS count, tags.id, tags.name')
and then look at the counts:
tags.map(&:count)
You'll see the array of counts that you're expecting.
Update: The original version of this answer mistakenly characterized select and subsequent versions ended up effectively repeating the current version of the other answer from #muistooshort. I'm leaving it in it's current state because it has the information about using raw sql. Thanks to #muistooshort for pointing out my error.
Although your query is in fact working as explained by the other answer, you can always execute raw SQL as an alternative.
There are a variety of select_... methods you can choose from, but I would think you'd want to use select_all. Assuming the build query that you implicitly generated was correct, you can just use that, as in:
ActiveRecord::Base.connection.select_all('
SELECT count(tags.id) AS count, tags.id, tags.name FROM `tags`
INNER JOIN `quote_tags` ON `quote_tags`.`tag_id` = `tags`.`id`
GROUP BY quote_tags.tag_id
ORDER BY count desc')
See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html for information on the various methods you can choose from.
There is a scope like this:
scope :sorted_by_dongle_count,
> { includes(:dongles)
.order('count(dongles.id) ASC')
.group('organisations.id')
.references(:dongles) }
This works perfectly well for SQLite, but fails for MySQL.
ActiveRecord::StatementInvalid: Mysql2::Error: Expression #8 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'portal_development.dongles.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by: [query here]
Looking at the query, it is easy to see why:
SELECT `organisations`.`id` AS t0_r0,
`organisations`.`name` AS t0_r1,
`dongles`.`id` AS t1_r0, 👈 This should be an aggregate!
`dongles`.`name` AS t1_r1,
`dongles`.`organisation_id` AS t1_r4
FROM `organisations`
LEFT OUTER JOIN `dongles`
ON `dongles`.`organisation_id` = `organisations`.`id`
GROUP BY organisations.id
ORDER BY count(dongles.id) DESC
Is there a proper way to do this without resorting to select() and hard-coding the entire list of columns?
(One other workaround I already know about is count caching. I am considering that already, as having the count readily available would speed up the sorting.)
I found that if I change to:
scope :sorted_by_dongle_count,
-> { joins(:dongles) 👈
.order('count(dongles.id) ASC')
.group('organisations.id')
.references(:dongles) }
I stop getting the error and the results were the same. So I guess only when using counts or other aggregates, avoid using includes? But for all other queries, use includes...
I have 2 paramters ( $memberparamter and $rest_id), that i am getting from the user. But every time my server runs the statement, it does not find anything. I have double checked with my database, and it says the desired output, does exist. If i delete one of the where clause, it works great.
Have i done it the wrong way?
This is my sql statement:
SELECT
eso.order_id as order_id,
eso.member_id as member_id,
esoi.title as title,
dl.used_date as checked,
dl.order_item_id as order_item_id
FROM exp_store_orders as eso
inner join exp_store_order_items as esoi on (eso.order_id = esoi.order_id)
inner join exp_deal_keys as dl on dl.order_item_id = esoi.order_item_id
where eso.member_id = '$memberparamter' and esoi.entry_id = '$rest_id'
and eso.order_paid > 0
group BY eso.transaction_id
ORDER BY eso.transaction_id desc
You need to specify which where clause fixes the problem. If I were to speculate, I would guess that you misspelled '$memberparamter' and it really should be '$memberparameter' -- on the belief that you would spell "parameter" correctly in your code.
Is it the GROUP BY that is causing the problem. Why do you have GROUP BY when you are not aggregating any of your SELECT columns?
I have a little query, it goes like this:
It's slightly more complex than it looks, the only issue is using the output of one subquery as the parameter for an IN clause to generate another. It works to some degree - but it only provides the results from the first id in the "IN" clause. Oddly, if I manually insert the record ids "00003,00004,00005" it does give the proper results.
What I am seeking to do is get second level many to many relationship - basically tour_stops have items, which in turn have images. I am trying to get all the images from all the items to be in a JSON string as 'item_images'. As stated, it runs quickly, but only returns the images from the first related item.
SELECT DISTINCT
tour_stops.record_id,
(SELECT
GROUP_CONCAT( item.record_id ) AS in_item_ids
FROM tour_stop_item
LEFT OUTER JOIN item
ON item.record_id = tour_stop_item.item_id
WHERE tour_stop_item.tour_stops_id = tour_stops.record_id
GROUP BY tour_stops.record_id
) AS rel_items,
(SELECT
CONCAT('[ ',
GROUP_CONCAT(
CONCAT('{ \"record_id\" : \"',record_id,'\",
\"photo_credit\" : \"',photo_credit,'\" }')
)
,' ]')
FROM images
WHERE
images.attached_to IN(rel_items) AND
images.attached_table = 'item'
ORDER BY img_order ASC) AS item_images
FROM tour_stops
WHERE
tour_stops.attached_to_tour = $record_id
ORDER BY tour_stops.stop_order ASC
Both of these below answers I tried, but it did not help. The second example (placing the entire first subquery inside he "IN" statement) not only produced the same results I am already getting, but also increased query time exponentially.
EDIT: I replaced my IN statement with
IN(SELECT item_id FROM tour_stop_item WHERE tour_stops_id = tour_stops.record_id)
and it works, but it brutally slow now. Assuming I have everything indexed correctly, is this the best way to do it?
using group_concat in PHPMYADMIN will show the result as [BLOB - 3B]
GROUP_CONCAT in IN Subquery
Any insights are appreciated. Thanks
I am surprised that you can use rel_items in the subquery.
You might try:
concat(',', images.attached_to, ',') like concat('%,', rel_items, ',%') and
This may or may not be faster. The original version was fast presumably because there are no matches.
Or, you can try to change your in clause. Sometimes, these are poorly optimized:
exists (select 1
from tour_stop_item
where tour_stops_id = tour_stops.record_id and images.attached_to = item_id
)
And then be sure you have an index on tour_stop_item(tour_stops_id, item_id).
Consider following two tables:
tag_names (tag_id, tag_name)
tag_links (tag_id, image_id)
An image can have multiple tags, I want to select all tags for a specific image id.
I am trying following query, but it doesnt seem to select correctly (selects only one row), What is wrong with it?
SELECT tag_name
FROM tag_names
LEFT JOIN tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
Edit: I'm using CodeIgniter Active record query, but I wrote in basic SQL format so that if someone is not fimiliar with CodeIgniter can help. However, this query works fine with simple mysql format (without using CodeIgniter) but strangely does not work with CodeIgniter, even there is no any problem with the syntax, it just selects one row.
Here is CodeIgniter Syntax:
$this->db->select('tag_name');
$this->db->from('tag_names');
$this->db->join('tag_links', 'tag_links.tag_id = tag_names.tag_id', 'left');
$this -> db -> where('tag_links.image_id', (int)$image_id);
$query = $this->db->get();
Try this:
SELECT tag_name
FROM tag_names
LEFT JOIN tag_links
ON tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
IMHO you forgot to join table (properly with ON statement) you are using.
EDIT: I have 2 ideas how to get rid of the problem:
First:
Change the line with SELECT
$this->db->select('tag_names.tag_name');
Second:
Use select() function with complete query:
$this->db->select($query, false);
$this->db->select() accepts an optional second parameter. If you set
it to FALSE, CodeIgniter will not try to protect your field or table
names with backticks. This is useful if you need a compound select
statement.
from: http://codeigniter.com/user_guide/database/active_record.html#select
It seems that you have a syntax error (you forgot tag_links in JOIN clause). By the way in my opinion you don't need LEFT JOIN for this purpose otherwise you may get incorrect results.
SELECT tag_name
FROM
tag_names
JOIN tag_links ON tag_links.tag_id = tag_names.tag_id
WHERE tag_links.image_id = $image_id
SELECT tag_names.tag_name
FROM tag_links
LEFT JOIN tag_names.tag_id = tag_links.tag_id
WHERE tag_links.image_id = $image_id
tag_names is only going to have single entry for a given ID, which means your query will return a single result. You need to primarily select from tag_links and then join the name of the tag on top of it, so you correctly select from the table with the multiple entries.