I've got the following scope on the Project model to find projects to which at least 80 crowdfunding pledges have been done or projects which have reached their goal already:
class Project < ActiveRecord::Base
scope :popular, -> { collecting.where("80 <= (?) OR goal <= (?)",
Pledge.select('COUNT(*)').where("project_id = projects.id").where(paid: true),
Pledge.select('SUM(amount)').where("project_id = projects.id").where(paid: true))
}
(...)
end
This works just fine and produces the following SQL for Postres:
Project.popular
# SELECT "projects".* FROM "projects" WHERE "projects"."state" = 'collecting' AND (80 <= (SELECT COUNT(*) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't') OR goal <= (SELECT SUM(amount) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't'))
It's important to get the count as well:
Project.popular.count
# SELECT COUNT(*) FROM "projects" WHERE "projects"."state" = 'collecting' AND (80 <= (SELECT COUNT(*) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't') OR goal <= (SELECT SUM(amount) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't'))
Okay, the subselects are nice, but I have this feeling that there's a better and more efficient way to do this. I've tried joins and sum, but the mandatory group to use aggregate functions breaks Project.popular.count.
Any ideas how to refactor this? Maybe just a way to do where("project_id = projects.id") in Hash notation?
I don't know if this is any good for you because I can't express the below query in the ActiveRecord lingo but if you can it will be a big improvement over the poor query currently being generated
select "projects".*, project_count, project_amount
from
"projects"
inner join (
select
id,
count(*) as project_count,
sum(amount) as project_amount
from pledges
group by id
where paid
) pledges using (id)
where
"projects"."state" = 'collecting'
and
(project_count >= 80 or project_amount >= goal)
Thanks, #Clodonaldo! With a few litte touches, your query works like a charm:
select "projects".*, pledges_count, pledges_sum
from
"projects"
inner join (
select
project_id as id,
count(*) as pledges_count,
sum(amount) as pledges_sum
from pledges
where paid
group by project_id
) pledges using (id)
where
"projects"."state" = 'collecting'
and
(pledges_count >= 80 or pledges_sum >= goal)
Not sure thou whether AR or AREL can build this.
UPDATE:
The shortest I can come up with is (I don't need the count and sum values):
Project.collecting.joins(
"INNER JOIN (",
Pledge.paid.group(:project_id).select("
project_id AS id,
COUNT(*) AS count,
SUM(amount) AS sum
").to_sql,
") pledges USING (id)"
).where("count >= 80 OR sum >= goal")
The above produces:
SELECT "projects".*
FROM
"projects"
INNER JOIN (
SELECT
project_id AS id,
COUNT(*) AS count,
SUM(amount) AS sum
FROM "pledges"
WHERE "pledges"."paid" = 't'
GROUP BY project_id
) pledges USING (id)
WHERE
"projects"."state" = 'collecting'
AND
(count >= 80 OR sum >= goal)
Related
this is my code :
SELECT DISTINCT Emps.name, Degrees.Name AS degree, Degrees.Date AS degree_date
FROM Emps INNER JOIN
Degrees ON Emps.id = Degrees.empId
but distict dosn't work , i want this result
i want distict name with degree which has max id or max date
thanks for all i found the solution
SELECT e.name, d.Name AS degree
FROM Emps AS e
full JOIN (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY t.empId ORDER BY t.id DESC) AS rn FROM Degrees AS t
) AS d ON e.id = d.empId
WHERE d.rn = 1
This works...
a = User.select("users.*, 1 as car_id").limit(2)
b = User.select("users.*, 1 as car_id").limit(2)
a.or(b)
>> returns a combined AR Relation
However,
a = User.select("users.*, 1 as car_id").limit(2)
b = User.select("users.*, 2 as car_id").limit(2)
a.or(b)
>> ArgumentError (Relation passed to #or must be structurally compatible. Incompatible values: [:select])
If I do a+b, it combines, but then converts to an array, and I would like to keep an AR relation to continue to do queries on.
When the select has different values for car_id, it can not union. I would have thought the same column name would allow the union.
I am creating virtual attribute (car_id) on the model, as each set of records needs car_id defined with a different value.
How do I solve the error and union with virtual attributes?
I want an ActiveRecord Relation, and interesting enough:
SELECT users.*, 1 as car_id
FROM users
UNION
SELECT users.*, 2 as car_id
FROM users
works fine when running raw sql.
SQL Example:
a.mail_for(:inbox).name
=> Mailboxer::Conversation::ActiveRecord_Relation
a.mail_for(:inbox).to_sql
"SELECT DISTINCT mailboxer_conversations.*, '102' as mailer_id, 'Listing' as mailer_type FROM \"mailboxer_conversations\" INNER JOIN \"mailboxer_notifications\" ON \"mailboxer_notifications\".\"conversation_id\" = \"mailboxer_conversations\".\"id\" AND \"mailboxer_notifications\".\"type\" IN ('Mailboxer::Message') INNER JOIN \"mailboxer_receipts\" ON \"mailboxer_receipts\".\"notification_id\" = \"mailboxer_notifications\".\"id\" WHERE \"mailboxer_notifications\".\"type\" = 'Mailboxer::Message' AND \"mailboxer_receipts\".\"receiver_id\" = 102 AND \"mailboxer_receipts\".\"receiver_type\" = 'Listing' AND \"mailboxer_receipts\".\"mailbox_type\" = 'inbox' AND \"mailboxer_receipts\".\"trashed\" = FALSE AND \"mailboxer_receipts\".\"deleted\" = FALSE ORDER BY \"mailboxer_conversations\".\"updated_at\" DESC"
So I pass an array of relations to union_scope...
ar= a.mail_for(:inbox)
br= b.mail_for(:inbox)
cr= c.mail_for(:inbox)
combined = union_scope([[a,ar],[b, br],[c, cr])
def union_scope(*relation)
combined = relation.first[1].none
relation.each do |relation_set|
mailer = relation_set[0]
scope = relation_set[1].select("#{relation_set[1].table_name}.*, \'#{mailer.id}\' as mailer_id, \'#{mailer.class.name}\' as mailer_type")
combined = combined.or(scope)
end
combined
end
Update:
def union_scope(*relation)
combined = relation.first[1].none
relation.each do |relation_set|
mailer = relation_set[0]
scope = relation_set[1].select("#{relation_set[1].table_name}.*, \'#{mailer.id}\' as mailer_id, \'#{mailer.class.name}\' as mailer_type")
combined = combined.union(scope)
end
conv = ::Mailboxer::Conversation.arel_table
::Mailboxer::Conversation.from(conv.create_table_alias(combined, :conversations).to_sql)
end
Resulted in this error:
Mailboxer::Conversation Load (5.8ms) SELECT "mailboxer_conversations".* FROM ( SELECT DISTINCT "mailboxer_conversations".* FROM "mailboxer_conversations" INNER JOIN "mailboxer_notifications" ON "mailboxer_notifications"."conversation_id" = "mailboxer_conversations"."id" AND "mailboxer_notifications"."type" IN ('Mailboxer::Message') INNER JOIN "mailboxer_receipts" ON "mailboxer_receipts"."notification_id" = "mailboxer_notifications"."id" WHERE "mailboxer_notifications"."type" = $1 AND "mailboxer_receipts"."receiver_id" = $2 AND "mailboxer_receipts"."receiver_type" = $3 AND (1=0) ORDER BY "mailboxer_conversations"."updated_at" DESC UNION SELECT DISTINCT mailboxer_conversations.*, '102' as mailer_id, 'Listing' as mailer_type FROM "mailboxer_conversations" INNER JOIN "mailboxer_notifications" ON "mailboxer_notifications"."conversation_id" = "mailboxer_conversations"."id" AND "mailboxer_notifications"."type" IN ('Mailboxer::Message') INNER JOIN "mailboxer_receipts" ON "mailboxer_receipts"."notification_id" = "mailboxer_notifications"."id" WHERE "mailboxer_notifications"."type" = $4 AND "mailboxer_receipts"."receiver_id" = $5 AND "mailboxer_receipts"."receiver_type" = $6 ORDER BY "mailboxer_conversations"."updated_at" DESC ) "conversations"
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: syntax error at or near "UNION")
LINE 1: ...ER BY "mailboxer_conversations"."updated_at" DESC UNION SELE...
^
: SELECT "mailboxer_conversations".* FROM ( SELECT DISTINCT "mailboxer_conversations".* FROM "mailboxer_conversations" INNER JOIN "mailboxer_notifications" ON "mailboxer_notifications"."conversation_id" = "mailboxer_conversations"."id" AND "mailboxer_notifications"."type" IN ('Mailboxer::Message') INNER JOIN "mailboxer_receipts" ON "mailboxer_receipts"."notification_id" = "mailboxer_notifications"."id" WHERE "mailboxer_notifications"."type" = $1 AND "mailboxer_receipts"."receiver_id" = $2 AND "mailboxer_receipts"."receiver_type" = $3 AND (1=0) ORDER BY "mailboxer_conversations"."updated_at" DESC UNION SELECT DISTINCT mailboxer_conversations.*, '102' as mailer_id, 'Listing' as mailer_type FROM "mailboxer_conversations" INNER JOIN "mailboxer_notifications" ON "mailboxer_notifications"."conversation_id" = "mailboxer_conversations"."id" AND "mailboxer_notifications"."type" IN ('Mailboxer::Message') INNER JOIN "mailboxer_receipts" ON "mailboxer_receipts"."notification_id" = "mailboxer_notifications"."id" WHERE "mailboxer_notifications"."type" = $4 AND "mailboxer_receipts"."receiver_id" = $5 AND "mailboxer_receipts"."receiver_type" = $6 ORDER BY "mailboxer_conversations"."updated_at" DESC ) "conversations"
While I cannot answer your actual question as to why these are incompatible I can offer a solution akin to your raw SQL example.
You can perform the same operation like so
user_table = User.arel_table
a = user_table.project(Arel.star, Arel.sql("1 as car_id")).take(2)
b = user_table.project(Arel.star, Arel.sql("2 as car_id")).take(2)
union = Arel::Nodes::UnionAll.new(a,b)
User.from(Arel::Nodes::As.new(union,user_table))
This will result in the following query.
SELECT
users.*
FROM
( (SELECT *, 1 as car_id
FROM users
ORDER BY
users.id ASC
LIMIT 2) UNION ALL (
SELECT *, 2 as car_id
FROM users
ORDER BY
users.id ASC
LIMIT 2)) AS users
Since this will still return an ActiveRecord::Relation you can still act on this object as you would any other we have simply substituted the normal data source (the users table) with your custom union data source of the same name.
Update based on extreme revision and some general assumptions as to what you actually meant to do
a= a.mail_for(:inbox)
b= b.mail_for(:inbox)
c= c.mail_for(:inbox)
combined = union_scope(a,b,c)
def union_scope(*relations)
base = build_scope(*relations.shift)
combined = relations.reduce(base) do |memo, relation_set|
Arel::Nodes::UnionAll.new(memo,build_scope(*relation_set))
end
union = Arel::Nodes::As.new(combined,::Mailboxer::Conversation.arel_table)
::Mailboxer::Conversation.from(union)
end
def build_scope(mailer,relation)
relation.select(
"#{relation.table_name}.*,
'#{mailer.id}' as mailer_id,
'#{mailer.class.name}' as mailer_type"
).arel
end
I'm still kinda new to SQL; please spare me. I have an issue with trying to join a table or another view to my existing query below.
The last question I had asked refers to joining the two subqueries and giving them an alias in order for the join to work (something I still don't understand?) I'm trying to join a table and another subquery to the query below. But I'm not quite sure how I could continue adding subqueries to it.
I don't really understand how the alias names allowed the subqueries to be joined, and if I add another subquery, then I'll add another alias?
Any assistance you could provide would be much appreciated. Apologize in advanced for the formatting :/
Select
Sub4.DT as Date, Sub2.USOH, Sub2.USOHP, Sub2.OVXH, Sub2.OVXHP, Sub4.USOPP, Sub4.OVXPP
From
(Select
*
From
(Select
x.ID, x.Date, x.USOH, x.OVXH, convert(varchar, x.Date, 1) AS DT,
Abs(Cast((((x.USOH / NullIf((y.USOH), 0)) - 1) * 100) AS Decimal(10, 2))) AS USOHP2,
Format(Abs(((x.USOH / NullIf((y.USOH), 0))) - 1), 'P') AS USOHP,
Abs(Cast((((x.OVXH / NullIf((y.OVXH), 0)) - 1) * 100) AS Decimal(10, 2))) AS OVXHP2,
Format(Abs(((x.OVXH / NullIf((y.OVXH),0)))-1),'P') AS OVXHP
From
(Select
a.Date as aDate, Max(b.Date) As aPrevDate
From
MACDHistogram A
Inner Join
MACDHistogram b on a.Date > b.Date
Group By a.Date) Sub1 -- Group Date > Previous Date SUB1
Inner Join
MACDHistogram x on Sub1.aDate = x.Date
Inner Join
MACDHistogram y on Sub1.aPrevDate = y.Date) T2) Sub2 --Histogram Percent SubQuery SUB2
Inner Join
(Select
*
From
(Select
z.ID, z.ID2, z.Date, z.USO as USOP, z.OVX as OVXP,
convert(varchar, z.Date, 1) as DT,
Cast(((z.USO / NullIf((q.USO),0)- 1) * 100) as Decimal(10,2)) AS USOPP2,
Format(((z.USO / NullIf((q.USO),0))-1),'P') AS USOPP,
Cast(((z.OVX / NullIf((q.OVX),0)- 1) * 100) as Decimal(10,2)) AS OVXPP2,
Format(((z.OVX / NullIf((q.OVX),0))-1),'P') AS OVXPP
From
(Select
c.Date as cDate, Max(d.Date) As cPrevDate
From
Prices C
Inner Join
Prices d on c.Date > d.Date Group By c.Date) Sub3 -- Group Date > Previous Date SUB3
Inner Join
Prices z on Sub3.cDate = z.Date
Inner Join
Prices q on Sub3.cPrevDate = q.Date) T4) Sub4 -- Price Percent Subquery SUB4
On Sub2.Date = Sub4.Date
Order By
sub4.Date Desc
I am using ROWNUM for fetching 999 rows in the following manner:
SELECT COUNT(*)
FROM PS_MMC_JOBDSSOA_MV JOB, PS_MMC_PERDSSOA_MV PER
WHERE PER.EMPLID = JOB.EMPLID AND
PER.ASOFDATE = (SELECT MAX(PER1.ASOFDATE) FROM PS_MMC_PERDSSOA_MV PER1
WHERE PER1.EMPLID = PER.EMPLID AND PER1.ASOFDATE <= SYSDATE) AND
JOB.ASOFDATE = (SELECT MAX(JOB1.ASOFDATE) FROM PS_MMC_JOBDSSOA_MV JOB1
WHERE JOB1.EMPLID = JOB.EMPLID AND JOB1.ASOFDATE <=SYSDATE) AND ROWNUM<1000;
I have re wrote the above sql query using left outer join:
SELECT COUNT(*)
FROM PS_MMC_JOBDSSOA_MV JOB LEFT OUTER JOIN PS_MMC_PERDSSOA_MV PER
ON (PER.EMPLID = JOB.EMPLID)
AND PER.ASOFDATE = ((SELECT MAX(PER1.ASOFDATE) FROM PS_MMC_PERDSSOA_MV PER1 WHERE
PER1.EMPLID = PER.EMPLID AND PER1.ASOFDATE <= SYSDATE)
AND JOB.ASOFDATE = (SELECT MAX(JOB1.ASOFDATE) FROM PS_MMC_JOBDSSOA_MV JOB1 WHERE
JOB1.EMPLID = JOB.EMPLID AND JOB1.ASOFDATE <= SYSDATE)) WHERE ROWNUM <1000;
ROWNUM is not working with Outer join. I am getting more than 1000 rows.
Could anybody suggest what I am doing wrong.
I have found this below sql query which is working with ROWNUM
SELECT * from (SELECT JOB.DEPTID, JOB.EMPL_STATUS, JOB.LOCATION, JOB.MMC_LOCATION_DESCR, JOB.CITY, JOB."STATE", PER."NAME" FROM
PS_MMC_JOBDSSOA_MV JOB LEFT OUTER JOIN PS_MMC_PERDSSOA_MV PER ON (PER.EMPLID = JOB.EMPLID)
AND PER.ASOFDATE = (SELECT MAX(PER1.ASOFDATE) FROM PS_MMC_PERDSSOA_MV PER1
WHERE PER1.EMPLID = PER.EMPLID AND PER1.ASOFDATE <= SYSDATE) AND JOB.ASOFDATE =
(SELECT MAX(JOB1.ASOFDATE) FROM PS_MMC_JOBDSSOA_MV JOB1 WHERE JOB1.EMPLID = JOB.EMPLID AND JOB1.ASOFDATE <= SYSDATE))
where ROWNUM <1000
I'm struggling to figure out the issue with my SQL table using compute sum.
All that is displayed where the sum of the column should be is a blank box!
Code Below:
TTITLE CENTER ==================== SKIP 1-
CENTER 'U T O O L' skip 1-
CENTER ==================== SKIP 1 -
LEFT 'Tool Report 1.03' SKIP 1 -
LEFT ============ SKIP 2-
RIGHT 'Page:' -
FORMAT 999 SQL.PNO SKIP 2
set pagesize 50
column MEMBERNAME HEADING 'Member Name' format a20
compute sum of TOTAL on Rental_ID
Break on RENTAL_ID
select Member.Member_ID, SUBSTR(Member.FName,0,10) || SUBSTR(' ',0,10) ||
SUBSTR(Member.SName,0,15) as MEMBERNAME,
Rental.Rental_ID,
Tool.Name,
Rental_Line.Qty,
Rental_Line.Price,
TO_Char(Rental_Line.Qty*Rental_Line.Price,'L9,999.99') TOTAL
from Rental_Line
INNER JOIN Rental
on Rental.Rental_ID = Rental_Line.Rental_ID
INNER JOIN Member
on Rental.Member_ID = Member.Member_ID
INNER JOIN Tool_Instance
on Rental_Line.Tool_Instance_ID = Tool_Instance.Tool_Instance_ID
INNER JOIN Tool
on Tool_Instance.Tool_ID = Tool.Tool_ID
where Rental.Rental_ID = '&Rental_ID';
may be this help you, as I understood you need SUM(Rental_Line.Qty) OVER (PARTITION BY Rental.Rental_ID)
select Member.Member_ID,
SUBSTR(Member.FName, 0, 10) || SUBSTR(' ', 0, 10) ||
SUBSTR(Member.SName, 0, 15) as MEMBERNAME,
Rental.Rental_ID,
Tool.Name,
Rental_Line.Qty,
Rental_Line.Price,
TO_Char(Rental_Line.Qty * Rental_Line.Price, 'L9,999.99') TOTAL,
SUM(Rental_Line.Qty) OVER (PARTITION BY Rental.Rental_ID) TOTAL_QTY,
SUM(Rental_Line.Qty * Rental_Line.Price) OVER (PARTITION BY Rental.Rental_ID) TOTAL_SUM
from Rental_Line
INNER JOIN Rental on Rental.Rental_ID = Rental_Line.Rental_ID
INNER JOIN Member on Rental.Member_ID = Member.Member_ID
INNER JOIN Tool_Instance on Rental_Line.Tool_Instance_ID =
Tool_Instance.Tool_Instance_ID
INNER JOIN Tool on Tool_Instance.Tool_ID = Tool.Tool_ID
where Rental.Rental_ID = '&Rental_ID';