What is the conceptual difference between User.first.followers.count and User.first.followers.to_a.count? - rails-activerecord

The two Active Record queries: User.first.followers.count and User.first.followers.to_a.count evaluate to the following SQL queries respectively:
[4] pry(main)> User.first.followers.count
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 4
[5] pry(main)> User.first.followers.to_a.count
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ? [["followed_id", 1]]
=> 4
How are they conceptually different?
This question came up in an exercise question at the end of section 14.1 in The Rails Tutorial
He gives the following hint:
Hint: Suppose that the user had a million followers.

Related

How to use GREATEST function with Over Partition by in Oracle

In the below code I want to select customer_name, location, gender and address along with customerid, aread_code.
select
customerid, aread_code, GREATEST(MAX(productid), MAX(itemid))
from
CUSTOMER C
inner join
ORDER O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group
customerid, by aread_code;
I tried GREATEST function along with OVER PARTITION BY to display required columns. It's throwing an error.
Could you please help me to select the required columns.
Thank you.
DISCLAIMER:
When working with more than one table, qualify the columns with their table name. You haven't done so, so we don't know what of the two tables the aread_code resides in. In my answer here I assume it is the customer's area. If it isn't then you need a different answer.
ANSWER:
You group by customer_id and area code. This gives you one row per customer. And you want the maximum product/item ID from the orders table. (I suppose they are drawn from the same sequence, so you can use this ID somehow to go on from there.)
The easiest approach for this is to get the maximum ID in a subquery. Either directly in the select clause or in the from clause.
Here is how to do this in the SELECT clause:
select
c.*,
(
select greatest(max(productid), max(itemid))
from orders o
where o.custid = c.custid
) as max_id
from customer c
where c.custtype = 'EXECUTIVE';
Here is one way to do this in the FROM clause:
select
c.*,
agg.max_id
from customer c
outer apply
(
select greatest(max(productid), max(itemid)) as max_id
from orders o
where o.custid = c.custid
) agg
where c.custtype = 'EXECUTIVE';
And here is another way to do this in the FROM clause:
select
c.*,
agg.max_id
from customer c
left outer join
(
select
custid,
greatest(max(productid), max(itemid)) as max_id
from orders
group by custid
) agg on agg.custid = c.custid
where c.custtype = 'EXECUTIVE';
If you only want customers with at least one order, then I recommend the approach with the FROM clause. You'd have to turn the OUTER APPLY into a CROSS APPLY resp. the LEFT OUTER JOIN into an INNER JOIN for this.
There are several mistakes in your code. The main confusion is not using table alias prefix for columns. There is a group by mistake and a problem with your table name ORDER - if it is a name of a table. ORDER is a reserved word in Oracle and if it is the name of the table then you should use something like "YOUR_OWNER_NAME"."ORDER".... Here is the corected code with some sample data and result:
WITH
customers (CUSTID, PRODUCTID, AREAD_CODE, CUSTOMER_NAME, LOCATION, GENDER, ADDRESS, CUSTTYPE) AS
(
Select 1, 1, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 2, 1, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 3, 3, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual Union All
Select 4, 7, 63, 'Name 1', 'Location 1', 'M', 'Address 1', 'EXECUTIVE' From Dual
),
orders (ORDER_ID, CUSTID, ITEMID, SOME_COLUMN) AS
(
Select 1, 1, 1, 'Some other data' From Dual Union All
Select 2, 2, 1, 'Some other data' From Dual Union All
Select 3, 3, 1, 'Some other data' From Dual Union All
Select 4, 3, 3, 'Some other data' From Dual Union All
Select 5, 4, 1, 'Some other data' From Dual Union All
Select 6, 4, 8, 'Some other data' From Dual
)
select
c.custid, c.aread_code, GREATEST(MAX(c.productid), MAX(o.itemid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group by
c.custid, c.aread_code
CUSTID AREAD_CODE MAX_ID
---------- ---------- ----------
1 63 1
4 63 8
3 63 3
2 63 1
There are different options to get the rest of the columns depending on your actual data you could use some or all of them.
Option 1 - select and group by as suggested in Beefstu's comment below
select Distinct
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code,
GREATEST(MAX(c.productid), MAX(o.itemid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
group by
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code
order by c.custid
CUSTID CUSTOMER_NAME LOCATION ADDRESS GENDER CUSTTYPE AREAD_CODE MAX_ID
---------- ------------- ---------- --------- ------ --------- ---------- ----------
1 Name 1 Location 1 Address 1 M EXECUTIVE 63 1
2 Name 1 Location 1 Address 1 M EXECUTIVE 63 1
3 Name 1 Location 1 Address 1 M EXECUTIVE 63 3
4 Name 1 Location 1 Address 1 M EXECUTIVE 63 8
Option 2. - using analytic functions MAX() OVER() with Distinct keyword (could be performance costly with big datasets) - result is the same as above
select Distinct
c.custid, c.customer_name, c.location, c.address, c.gender, c. custtype, c.aread_code,
GREATEST(MAX(c.productid) OVER(Partition By c.custid), MAX(o.itemid) OVER(Partition By c.custid)) "MAX_ID"
from
CUSTOMERS C
inner join
ORDERS O ON c.custid = o.custid
where
c.custtype = 'EXECUTIVE'
order by c.custid
Option 3 - using left join to a subquery - see the solution offered by Thorsten Kettner

Query to retrieve rows with no data even if not satisfying the where clause condition in Oracle

I have a query two tables - TableA , TableB
Query :
select a.name, count(b.code)
from tableA a
join tableA b on b.id = a.id
where a.name = ('AA','BB','WWW')
group by a.name;
The data is available only for AA so it displays the result as
AA 5
But i want to display the data as :
AA 5
BB 0
WWW 0
You can collect expected names into separate table/subquery and then perform a left join with your data tables like
with exp_names as (
select 'AA' name from dual
union all
select 'BB' from dual
union all
select 'WWW' from dual
)
select en.name, count(b.code)
from exp_names en
left join tableA a on en.name = a.name
left join tableB b on b.id = a.id
group by en.name;
fiddle

Join Table with oracle sql

I am newbie for Oracle SQL. I wish to join 2 tables.
In TABLE 1 two tables had been join with the below presentation
Here is the sql script for TABLE 1:
select a.ID,
a.CLASSIFICATION_CODE,
a. CATEGORY_CODE
from a, b
where locality = 'xxx'
and a.DIV = b.DIV
and a.TYPE = b.TYPE
and a.DIST = b.DIST
and a.BS = b.BS
and a.LOT = b.LOT;
TABLE 2
Here is the sql script for TABLE 2:
select CODE_TYPE,CODE_1,CODE_DESC from PUBCODE01
where PUBCODE01.code_type IN ('LCL','LCA');
TABLE 3 (the table for final result after join 3 tables)
I need help on how could I combined the two sql script in order to produce the TABLE 3 presentation.
You may need to join twice the same table, based on the code_ty; for example:
SQL> with query1(id, classification_code, category_code) as(
2 select 123, 1, 3 from dual union all
3 select 456, 2, 4 from dual union all
4 select 789, 1, 3 from dual
5 ),
6 query2(code_type, code_1, code_desc) as (
7 select 'LCL', 1, 'TOMATOES' from dual union all
8 select 'LCL', 2, 'DURIAN' from dual union all
9 select 'LCA', 3, 'VEGETABLE' from dual union all
10 select 'LCA', 4, 'FRUITS' from dual
11 )
12 select id, LCL.code_desc, LCA.code_desc
13 from query1
14 inner join query2 LCL
15 on(LCL.code_1 = query1.classification_code)
16 inner join query2 LCA
17 on(LCA.code_1 = query1.category_code);
ID CODE_DESC CODE_DESC
---------- --------- ---------
123 TOMATOES VEGETABLE
789 TOMATOES VEGETABLE
456 DURIAN FRUITS
or even the following, depending on what your data can be:
select id, LCL.code_desc, LCA.code_desc
from query1
inner join query2 LCL
on(LCL.code_1 = query1.classification_code
and LCL.code_type = 'LCL')
inner join query2 LCA
on(LCA.code_1 = query1.category_code
and LCA.code_type = 'LCA')
As an aside, you should better use ANSI join; your query should be:
select a.ID,
a.CLASSIFICATION_CODE,
a. CATEGORY_CODE
from a
inner join b
on(
a.DIV = b.DIV
and a.TYPE = b.TYPE
and a.DIST = b.DIST
and a.BS = b.BS
and a.LOT = b.LOT
)
where locality = 'xxx'
Tomatoes are a fruit, please research your produce before posting.

How to handle singe-row subquery returns more than one row error with case statement

I am building an Oracle query which has a case statement involved.
SELECT
CASE WHEN
((SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate)) > 0)
/*The below subquery returns 1 row*/
THEN (SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate))
ELSE
/* The below subquery returns 2 rows*/
(SELECT child_agent_id FROM agent_object_group_member WHERE parent_agent_id IN
(SELECT agent_or_group_id FROM trans_slot WHERE slot_id IN
(SELECT slot_id FROM trans_slot WHERE slot_alias = 'PP' AND measure_expiration > sysdate)
)
)
END
"Agent_ID" from DUAL;
When the run the subqueries independent they run fine. But running the whole query returns
ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
*Cause:
*Action:
If I understand what you're after, you can't do it like that. You'll need to use a left outer join and then chose the column value you want to display.
Here's a simplified example based on the SQL you provided:
WITH t1 AS (SELECT 1 ID FROM dual UNION ALL
SELECT 0 ID FROM dual UNION ALL
SELECT 2 ID FROM dual),
t2 AS (SELECT 10 child_id, 0 parent_id FROM dual UNION ALL
SELECT 20 child_id, 0 parent_id FROM dual UNION ALL
SELECT 30 child_id, 1 parent_id FROM dual UNION ALL
SELECT 40 child_id, 2 parent_id FROM dual)
---- end of mimicking two tables with the sample data in them. See the query below:
SELECT COALESCE(t2.child_id, t1.id) ID
FROM t1
LEFT OUTER JOIN t2 ON (t1.id = t2.parent_id AND t1.id = 0);
ID
----------
10
20
2
1
Here, I have used the t1 and t2 subqueries to mimic the output you'd get from your main subqueries in your original query.
Then we outer join t2 to t1 only where the t1.id = 0. By doing this, you can then simply choose the t2.child_id value if it exists, otherwise use the t1.id value.
(I realise that in your example, the t1 equivalent subquery would only generate 1 row, based on what you said, but I've included 3 rows so that you can see what the results would be based on the different ids.)
ETA:
In your case, the t1 subquery in my example above would be:
SELECT agent_or_group_id
from trans_slot
where slot_id = (SELECT slot_id
from trans_slot
where slot_alias = 'PP'
and measure_expiration > sysdate)
and the t2 subquery would be:
SELECT child_agent_id
FROM agent_object_group_member
WHERE parent_agent_id IN (SELECT agent_or_group_id
FROM trans_slot
WHERE slot_id IN (SELECT slot_id
FROM trans_slot
WHERE slot_alias = 'PP'
AND measure_expiration > sysdate))

Rails include with multiple tables

Please anyone help me:
Using Rails I developed an application.
My models look like this:
class Score < ActiveRecord::Base
belongs_to :song
belongs_to :user
end
class Song < ActiveRecord::Base
has_one :gsong
has_many :scores
end
class Gsong < ActiveRecord::Base
belongs_to :song
end
class user < ActiveRecord::Base
has_many :scores
end
And my ScoresController:
class scoresController < ApplicationController
def index
id = current_user.id
#scores = score.where(:user_id => id)
render :json => {
:scores => #scores.as_json(:include => {:gsong => { :include => { :song => { :only => [:title, :album]}}, :only => [:artwork]}}, :only => [:song_id, :score]),
}
end
It's working fine functionally, But it's making too many queries in the database like below:
score Load (0.1ms) SELECT `scores`.* FROM `scores` WHERE `scores`.`user_id` = 3
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 8 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 8 LIMIT 1
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 2 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 2 LIMIT 1
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 1 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 1 LIMIT 1
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 11 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 11 LIMIT 1
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 12 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 12 LIMIT 1
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs` WHERE `gsongs`.`id` = 23 LIMIT 1
Song Load (0.1ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` = 23 LIMIT 1
How can I fetch all this data with single or two queries like:
Gsong Load (0.1ms) SELECT `gsongs`.* FROM `gsongs`
Song Load (0.3ms) SELECT `songs`.* FROM `songs` WHERE `songs`.`id` IN (8,2,1,11,12,23)
I would look into using ActiveModel serializers. I think you'll find it will make complex JSON like this a breeze
There's a great Screencast on it at
http://railscasts.com/episodes/409-active-model-serializers

Resources