I'm trying to write the following query using the ruby gem Squeel
SELECT COUNT(*)
FROM(
SELECT
a.end_at AS START,
Min(b.start_at) AS END
FROM periods AS a
JOIN periods AS b ON b.season_id IN (1,2,3) AND a.end_at <= b.start_at
WHERE a.season_id IN (1,2,3)
GROUP BY a.end_at
HAVING a.end_at < MIN(b.start_at)
) AS gaps
WHERE
gaps.START < '2013-05-17' AND gaps.END > '2013-05-05';
Any idea on how to achieve this?
I can get the seasons_ids using:
seasons = self.seasons
Period.where{season_id.in(seasons.select{id})}
but the self join with condition, I have no idea how to tackle this so far.
This is similar to what I've tried to do here.
What I would try is using a sub-query, joining Period to itself as follows:
class Period < ActiveRecord::Base
attr_accessible :end_at, :season_id, :start_at
belongs_to :season
scope :in_seasons, ->(season_ids) { joins{season}.where{id.in(season_ids)} }
def self.find_gaps(start_date, end_date)
season_ids = ["1", "2", "3"]
scope = select{[end_at.as(`gap_start`), `min(self_join.start_at)`.as(`gap_end`)]}
scope = scope.joins{"LEFT JOIN (" + Period.in_seasons(season_ids).to_sql + ") AS self_join ON self_join.end_at <= periods.start_at"}
scope = scope.where{(`self_join.start_at` != nil) & (`gap_start` < start_date) & (`gap_end` > end_date)}
scope = scope.group{`gap_end`}.having{end_at < `min(self_join.start_at)`}
end
end
In rails console, Period.find_gaps('2001-01-01', '2010-01-01').to_sql produces (formatting is my own):
SELECT
\"periods\".\"end_at\" AS gap_start,
min(self_join.start_at) AS gap_end
FROM \"periods\"
LEFT JOIN
(SELECT \"periods\".*
FROM \"periods\"
INNER JOIN \"seasons\" ON \"seasons\".\"id\" = \"periods\".\"season_id\"
WHERE \"periods\".\"id\" IN (1, 2, 3)
) AS self_join ON self_join.end_at <= periods.start_at
WHERE ((self_join.start_at IS NOT NULL AND gap_start < '2001-01-01' AND gap_end > '2010-01-01'))
GROUP BY gap_end
HAVING \"periods\".\"end_at\" < min(self_join.start_at)
It looks like what you are trying to get at... at least the inner portion.
Hope it helps.
Related
I am in a need of running a complex SQL query and because I didn't know how to construct the query using ActiveRecod, I had to use a raw SQL query by using find_by_sql:
scope :get_cars, -> do
find_by_sql('select * from
(SELECT cars.*,
manufacturer.company_name AS manufacturer_company_name,
services.a_num AS service_a_num,
(SELECT car_documents.file_url FROM car_documents
WHERE car_documents.car_id = cars.id AND car_documents.doc_type = 1 LIMIT 1) AS doc1_file_url,
(SELECT car_documents.file_s3_url FROM car_documents
WHERE car_documents.cart_id = cars.id AND car_documents.doc_type = 3 LIMIT 1) AS file_inv,
(SELECT car_data.demand_lvl FROM car_data
WHERE car_data.car_id = cars.id) AS demand_lvl,
(SELECT car_logs.invoice FROM car_logs
WHERE car_logs.car_id = cars.id AND car_logs.invoice = 1 LIMIT 1) AS invoice_sent
FROM "cars"
INNER JOIN "services" ON "services"."id" = "cars"."service_id"
LEFT JOIN manufacturers ON manufacturers.id = cars.manufacturer_id
WHERE (cars.status_id != 6
AND cars.delivery_date < NOW() - INTERVAL \'15 days\'
) ORDER BY cars.pickup_date ASC) t
Where doc1_file_url IS NULL OR file_inv IS NULL OR invoice_sent IS NULL')
end
The output is an array. How do I convert this array into an ActiveRecord object? Or possibly, is there any workaround?
I think, You can use the from() method from the Active Record interface. You can use a subquery as a table for a SQL select statement with help of from() method. check the below example. In this way, you will get the active record object.
subquery = Setting.limit(10)
Setting.from("(#{subquery.to_sql}) settings")
In your case,
subquery = "(SELECT cars.*,
manufacturer.company_name AS manufacturer_company_name,
services.a_num AS service_a_num,
(SELECT car_documents.file_url FROM car_documents
WHERE car_documents.car_id = cars.id AND car_documents.doc_type = 1 LIMIT 1) AS doc1_file_url,
(SELECT car_documents.file_s3_url FROM car_documents
WHERE car_documents.cart_id = cars.id AND car_documents.doc_type = 3 LIMIT 1) AS file_inv,
(SELECT car_data.demand_lvl FROM car_data
WHERE car_data.car_id = cars.id) AS demand_lvl,
(SELECT car_logs.invoice FROM car_logs
WHERE car_logs.car_id = cars.id AND car_logs.invoice = 1 LIMIT 1) AS invoice_sent
FROM "cars"
INNER JOIN "services" ON "services"."id" = "cars"."service_id"
LEFT JOIN manufacturers ON manufacturers.id = cars.manufacturer_id
WHERE (cars.status_id != 6
AND cars.delivery_date < NOW() - INTERVAL \'15 days\'
) ORDER BY cars.pickup_date ASC)"
i'm working with oracle 11g and we have a problem, well, this query takes forever to execute, the main table tbl_inc has about 17 million records, is there any way i can improve this query?
i can't add indexes, i don't have privileges for that.
SELECT count(*) FROM TBL_INC INC LEFT JOIN TBL_PDS P ON INC.SID = P.TRX
INNER JOIN TBL_ASTS ASTS ON ASTS.CODE = INC.CODE AND ASTS.MIT
= INC.MIT AND ASTS.OPE_PROD = 3
WHERE (INC.INC_DATE -1) >= to_date('29/10/20', 'DD/MM/YY')
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY')
AND INC.OPE = 50 AND SUBSTR(INC.CARD_NMBR, 1, 6) = 123456
AND INC.MIT='05' AND INC.CODE='00';
thanks
First, I would rewrite this so the where clause does not have expressions on the columns:
SELECT count(*)
FROM TBL_INC INC LEFT JOIN
TBL_PDS P
ON INC.SID = P.TRX INNER JOIN
TBL_ASTS ASTS
ON ASTS.CODE = INC.CODE AND
ASTS.MIT = INC.MIT AND
ASTS.OPE_PROD = 3
WHERE INC.INC_DATE >= to_date('29/10/20', 'DD/MM/YY') + interval '1 day' AND
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY') + interval '1 day' AND
INC.OPE = 50 AND
INC.CARD_NMBR LIKE '123456%' AND
INC.MIT = '05' AND INC.CODE = '00';
Then for this query, you want an index on: TBL_INC(MIT, CODE, OPE, INC_DATE, CARD_NMBR). I am guessing that you have indexes on the JOIN keys used in the other tables, but those would be TBL_PDS(TRX) and TBL_ASTS(CODE< MIT, OPE_PROD).
It is a very good practice to reduce the number of the rows from the join, rather that apply a 'where' condition to the end
SELECT count(1) FROM TBL_INC INC
INNER JOIN TBL_ASTS ASTS
ON ASTS.CODE = INC.CODE
AND ASTS.MIT = INC.MIT
AND ASTS.OPE_PROD = 3
AND INC.OPE = 50
AND SUBSTR(INC.CARD_NMBR, 1, 6) = 123456
AND INC.MIT='05'
AND INC.CODE='00'
AND (INC.INC_DATE -1) >= to_date('29/10/20', 'DD/MM/YY')
AND INC.INC_DATE - 1 <= to_date('05/11/20', 'DD/MM/YY')
LEFT JOIN
TBL_PDS P
ON INC.SID = P.TRX
I currently have the following sql (Generated by typeorm).
SELECT
"media"."permissionOwner" AS "media_permissionOwner",
"media"."permissionGroup" AS "media_permissionGroup",
"media"."permissionOther" AS "media_permissionOther",
"media"."id" AS "media_id",
"media"."filename" AS "media_filename",
"media"."mime" AS "media_mime",
"media"."description" AS "media_description",
"media"."length" AS "media_length",
(
SELECT
1
FROM
"user" "user"
WHERE
"user"."id" = $1 LIMIT 1
)
AS "user"
FROM
"media_object" "media"
LEFT JOIN
"media_object_groups_group" "media_groups"
ON "media_groups"."mediaObjectId" = "media"."id"
LEFT JOIN
"group" "groups"
ON "groups"."id" = "media_groups"."groupId"
LEFT JOIN
"media_object_owner_user" "media_owner"
ON "media_owner"."mediaObjectId" = "media"."id"
LEFT JOIN
"user" "owner"
ON "owner"."id" = "media_owner"."userId"
WHERE
(
"media"."permissionGroup" >= $2
AND
COUNT("groups") = 0 -- How can I accomplish this
)
OR
(
"media"."permissionGroup" >= $3
AND groups # > user_groups
)
OR
(
owner # > ARRAY[user]
AND "media"."permissionOwner" >= $4
)
OR "media"."permissionOther" >= $5 -- PARAMETERS: [23,4,4,4,4]
The problem is, I can't use HAVING, because it doesn't seem like it support this type of condition making like WHERE does.
I tried it with a subquery at the COUNT part, but than I can't access the "groups" alias inside the sub select count query.
I am not even sure if the other part of the query works, but first I want to solve the "count" issue.
I upgraded to Rails 3.2.3 and all of a sudden this code no longer works:
def self.search(query, project_id, person_id)
if query
where("number LIKE ?", "%#{query}%")
elsif project_id
where("project_id LIKE ?", project_id)
elsif person_id
where("projects.person_id = ?", person_id)
else
scoped
end
end
It is the last where clause that triggers the error:
SQLite3::SQLException: no such column: projects.person_id: SELECT COUNT(DISTINCT "invoices"."id") FROM "invoices" LEFT OUTER JOIN "items" ON "items"."invoice_id" = "invoices"."id" LEFT OUTER JOIN "payments" ON "payments"."invoice_id" = "invoices"."id" WHERE "invoices"."user_id" = 1 AND (projects.person_id = '1')
In my models all the belongs_to and has_many statements are set correctly and it worked in my previous version of Rails (not sure which one that was though).
Can anybody tell me how to get this working again?
Thanks for any help.
I believe you'll have to join the projects table:
..
elsif person_id
joins(:projects).where("projects.person_id = ?", person_id)
else
..
SELECT *
FROM jobs
WHERE (SELECT DISTINCT jobs.*
FROM jobs, job_requests
WHERE (jobs.user_id = 1) OR
(job_requests.user_id = 1 AND job_requests.job_id = jobs.id)
)
This sql gives me:
Mysql::Error: Operand should contain 1 column(s).
If I execute the select from the where clause it works
SELECT DISTINCT jobs.* FROM jobs, job_requests
WHERE (jobs.user_id = 1) OR
(job_requests.user_id = 1 AND job_requests.job_id = jobs.id)
Could somebody explain me why? This query is generated by rails activerecord so the main select is needed.
The ror code:
has_many :my_jobs, :class_name=>"Job", :finder_sql =>
'SELECT DISTINCT jobs.* ' +
'FROM jobs, job_requests ' +
'WHERE (jobs.user_id = #{id}) OR ' +
'(job_requests.user_id = #{id} AND job_requests.job_id = jobs.id AND job_requests.request_status IN ("requested", "confirmed") )'
I am submitting my answer based on the additional information provided. The query below should address your requirement.
Job.all(:conditions=> [ "
jobs.user_id = ? OR
EXISTS (
SELECT *
FROM job_requests AS B
WHERE B.job_id = jobs.id AND B.user_id = ?
)", user_id, user_id ]
)
If you want an efficient version of the same query then you should go with UNIONs.
sql = "
SELECT *
FROM jobs A WHERE A.user_id = ?
UNION
SELECT *
FROM jobs A, job_requests B
WHERE A.id = B.job_id AND B.user_id = ?
"
Job.find_by_sql(Job.send(:sanitize_sql_array, [sql, user_id, user_id]))
The first query can be converted to a named_scope.
class Job < ActiveRecord::Base
named_scope :for_user, lambda { |user_id| { :conditions=> [ "
jobs.user_id = ? OR
EXISTS (
SELECT *
FROM job_requests AS B
WHERE B.job_id = jobs.id AND B.user_id = ?
)", user_id, user_id ] }
}
end
Now you can use the named_scope as follows:
Jobs.for_user(current_user)
SQL query: Documentation
SELECT (
'95.86.122.224', NOW( ) , '95.86.122.224', '95.86.122.224'
)
FROM orders
LEFT JOIN visitors ON visitors.ip = '95.86.122.224'
WHERE visitors.ip IS NULL
LIMIT 0 , 30
MySQL said: Documentation
#1241 - Operand should contain 1 column(s)
If I have understood you correctly you need to restructure as
SELECT * FROM jobs
WHERE EXISTS
(SELECT DISTINCT jobs.*
FROM jobs, job_requests
WHERE (jobs.user_id = 1)
OR (job_requests.user_id = 1 AND job_requests.job_id = jobs.id))
I'm guessing you only want to select from jobs where the inner query is true, the above will do this for you.
Following code will work, if you want to get the jobs with job requests for a given user:
Job.all( :joins => :jobs_requests,
:conditions => ["job_requests.user_id = ?", user_id])