Is there a way to get the Distinct values from each column in a MYSQL database without having to do multple SELECT DISTINCT statements for each column?
Right now in my rails controller I'm using .pluck() to run:
#first = User.distinct.pluck(:first_name)
#last = User.distinct.pluck(:last_name)
#city = User.distinct.pluck(:city)
#state = User.distinct.pluck(:state)
#age = User.distinct.pluck(:age)
#info = {
'first' => #first,
'last' => #last,
'city' => #city,
'state' => #state,
'age' => #age
}
respond_with #info
Which creates an object with my two unique arrays in it, however it takes about 7.7 seconds (my table has 3.2 million fully-populated rows) and runs two separate SQL queries.
I tried this method, but this is giving me an array of each unique COMBINATION:
#info = User.distinct.select(:first_name, :last_name, :city, :state, :age)
respond_with #info
Not sure how you want your final data to be output, but if you want to avoid multiple SQL statements, you can do something like this:
SELECT
ARRAY(SELECT DISTINCT(col1) FROM MyTable) AS col1_values,
ARRAY(SELECT DISTINCT(col2) FROM MyTable) AS col2_values
This will give you a single row with all the DISTINCT values for each "colX" you specify. Just add on whatever additional columns you want to include. The DISTINCT values in each column will be returned as an array.
Performance will still be stinky, since I don't think you can avoid doing multiple distinct DISTINCT operations.
Let me know if that's what you're looking for.
You could do this:
#info = User.distinct.select(:first_name, :last_name, :city, :state, :age)
#first, #last, #city, #state, #age = [], [], [], [], []
#info.map{ |i| #first<<i[0]; #last<<i[1]; #city<<i[2]; #state<<i[3], #age<<i[4] }
#info = { 'first' => #first,
'last' => #last,
'city' => #city,
'state' => #state,
'age' => #age }
respond_with #info
Please try benchmarking before implementing this in production.
Related
I have two tables, A and B. A and B have a foreign key. Both ActiveRecord.
class A
has_one :b
def valueRendered
b.value1 || b.value2
end
end
class B
( belongs_to :a, :foreign_key => :keyb ) if self.b
end
Here the value coming from valueRendered method in A is having values from field value2 of B when value1 is null. I have to sort the table using the valueRendered, i.e., table should be sorted on the basis of values of both value1 and value2. How to proceed?
Edit:
To be more specific, suppose there is one calculated column (a method in rails) which has value from field "value1" but if value1 is null, the value comes from "value2". I want to sort the whole table (not limited to any specific no of records or scope) with respect to this calculated field (method). I am confused how to proceed.
Some more edit: Actual sample code
In the controller,
sort_fields = ['offer_orders.id','offers.name','offer_orders.created_at',
'offer_orders.country', 'offer_orders.ip_address',
'fraud_score_daily.adjusted_fraud_probability, fraud_score_daily.fraud_probability',
'players.email','players.account_status',
nil,
nil,
nil,
nil
]
arel = OfferOrder.includes(:offer, :player, :fraud_score_daily).group('fraud_score_daily.offer_order_id').order('dt desc')
arel = paginate_table_data(arel, :sort_fields => sort_fields)
paginate_table_data method is in a helper
def paginate_table_data(arel, opts={})
opts.assert_keys(:required => [:sort_fields])
#page_size = page_size
#current_page = current_page
arel.paginate(:page => #current_page, :per_page => params[:iDisplayLength]).
order(order_by(opts[:sort_fields]).join(', '))
end
How about something like:
scope :order_by_value_rendered, joins(:b).order("value1, value2")
I am retrieving data:
$mydata = $this->ProductList->find('all', array('order' => 'rand()', 'conditions' => array('name' => 'we love')));
I have set up a HABTM relationship to the Product model. As you can see, I am fetching all products in the 'we love'-list. Now, I want those Products I am retrieving to be randomised. But they are not, instead the MySQL is randomised on the ProductList model as you can see in the SQL. Why is that? How can I get the random fetch on the Products instead?
Resulting MySQL query:
SELECT `ProductList`.`id`, `ProductList`.`name` FROM `database`.`product_lists` AS `ProductList` WHERE `name` = 'we love' ORDER BY rand() ASC
SELECT `Product`.`id`, `Product`.`category_id`, `Product`.`name`, `Product`.`price`, `Product`.`description`, `ProductListsProduct`.`product_list_id`, `ProductListsProduct`.`product_id` FROM `database`.`products` AS `Product` JOIN `database`.`product_lists_products` AS `ProductListsProduct` ON (`ProductListsProduct`.`product_list_id` = 3 AND `ProductListsProduct`.`product_id` = `Product`.`id`)
EDIT:
There are so many different ways to approach this; to get a random product from a user's product list. You could do it with PHP - just find all of the products and then use rand() to pick on from the returned array. You could set a Model query condition. The list goes on...
I would probably create an alias to the Product model in ProductList called RandomProduct. You could set the query for the retrieved product when you set the relationship:
public $hasMany = array(
'RandomProduct' => array(
'className' => 'Product',
'foreignKey' => 'product_list_id',
'order' => 'Rand()',
'limit' => '1',
'dependent' => true
)
);
You can then use the containable behavior so that this model is only retrieved when you need it. (You wouldn't need to do this if recursive finds are greater than -1, but I usually do that as best practice so that my models only query for the data that they need.) The following would return any ProductList called 'we love' and a "random" product associated with that list.
$mydata = $this->ProductList->find(
'all',
array(
'conditions' => array(
'name' => 'we love'
)
),
'contain' => array(
'RandomProduct'
)
);
$conditions = Array
(
[table] => products_pages
[alias] => ProductsPage
[type] => inner
[foreignKey] =>
[conditions] => Array
(
[0] => ProductsPage.product_id = Product.id
)
)
I'm trying to set up NOT EXISTS conditions, like the following SQL statement:
SELECT * FROM products_pages,products
WHERE NOT EXISTS (SELECT id
from products_pages
where products_pages.product_id = products.id)
So basically select any product that doesn't exist in the products_pages table.
What is the proper way to format that SQL statement for CakePHP and replace it here:
[conditions] => Array
(
[0] => (What's the proper way to insert above SQL here?
)
Would really appreciate your help guys, I've been trying to figure this out for about 5 hours with no luck. Thanks!
You can always use query if you don't find the way to do it with CakePHP:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
In this case security wouldn't be compromised as you are not using any input.
Anyway, something simple would be just to do it in more than one step:
//selecting the products in the productcs_pages table
$productsWithPages = /* query to get them*/
//getting an array of IDs
$productsWidthPagesIds = Hash::extract($productsWithPages, '{n}.Product.id');
//doing the NOT IN to select products without pages
$productsWithoutPages= $this->Product->find('all',
array('conditions' =>
array( 'NOT' => array('Product.id' => $productsWidthPagesIds )
)
);
I'm using the following SELECT statement:
SELECT *
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
This is what I get back:
[id] => 2
[name] => flavor
[value] => chocolate
[id] => 2
[name] => type
[value] => cookie
This is the result I'd like to get:
[id] => 2
[flavor] => chocolate
[type] => cookie
Is there a way to combine my results so I don't have a bunch of rows referring to the same ID? If now, how should I handle this?
I'm using Modx and this is working with the Template Variable tables: http://wiki.modxcms.com/index.php/Template_Variable_Database_Tables
You can just use case statements:
SELECT
id,
MAX( CASE WHEN name = 'flavor' THEN value ELSE NULL END ) AS flavor,
MAX( CASE WHEN name = 'type' THEN value ELSE NULL END ) AS type
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
GROUP BY id
Of course, this approach only works if you know ahead of time what keys you want to select; but it seems like in this case you do know (flavor + type).
Problem is, you might have
{ "id": 2, "flavor": "chocolate", "type": "cookie" }
and another row with
{ "id": 3, "flavor": "vanilla", "calories": "375" }
...I don't think there's an easy way to solve the problem in MySQL; you'd need to decide what keys to look for.
On the other hand you can collapse the rows in PHP:
while($tuple = ...fetch tuple from mysql cursor...)
{
list ($id, $name, $value) = $tuple;
if (!isset($objs[$id]))
{
// You might want to add also "'id' => $id" to the array definition
$objs[$id]= array ($name => $value);
}
else
{
$obj = $objs[$id];
$obj[$name] = $value;
$objs[$id] = $obj;
}
}
Now $objs contains the desired data; you still need to get it back into Modx, though.
You can do this with a group by:
SELECT id,
max(case when name = 'flavor' then value end) as flavor,
max(case when name = 'type' then value end) as type
FROM prefix_site_tmplvars LEFT JOIN
prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id LEFT JOIN
prefix_site_content ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
group by id
Rails and MySQL:
I have a table with several boolean columns representing tags. I want to find all the rows for which a specific one of these columns is 'true' (or I guess in the case of MySQL, '1'). I have the following code in my view.
#tag = params[:tag]
#supplies = Supply.find(:all,
:conditions=>["? IS NOT NULL and ? !=''", #tag, #tag], :order=>'name')
The #tag is being passed in from the url. Why is it then that I am instead getting all of my #supplies (i.e. every row) rather than just those that are true for the column for #tag.
Thanks!
If params[:tag] is set to foo, the find method is generating this query:
select * from supplies where 'foo' is not null and 'foo' != '' order by name;
This is returning all your Supply records because both conditions are always true.
'foo' is not null
'foo' != ''
Of course you're intending for params[:tag] to be a column name, but that is just terrible design.
You should have a tag attribute on your Supply model and use the following finder:
#supplies = Supply.all(:conditions => ["tag = ?", params[:tag]], :order => "name")
If you really want the ability for Supplies to have the option for multiple Tags, use:
class Supply < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :supplies
end
#supplies = Supplies.all(:conditions => {:tags => ['foo', 'bar']}, :order => "name")
I think this is what you want to do
#tag = params[:tag]
#supplies = Supply.find(:all, :conditions=>["? = ?", #tag, true], :order=>'name')