Can I have a standalone clause in ActiveRecord? - active-record-query

I want to factor out the where clause in my two queries like this:
where_clause = where("(created_at >= ? and created_at < ?) or (updated_at >= ? and updated_at < ?)", range_start_date, range_end_date, range_start_date, range_end_date)
#book_instances = BookInstance.limit(pagination.items_per_page).
offset((pagination.page - 1) * pagination.items_per_page).
where_clause.
order(:id)
#pagination.total_items = BookInstance.where_clause.count
But ActiveRecord is not happy. I thought you could break out the query bits independently.

I ended up doing this, though I'm still wondering if I'm missing another nicer way:
book_instances_in_date_range = BookInstance.where(
"(created_at >= ? and created_at < ?) or (updated_at >= ? and updated_at < ?)",
range_start_date, range_end_date, range_start_date, range_end_date)
#pagination.total_items = book_instances_in_date_range.count
#book_instances = book_instances_in_date_range.limit(pagination.items_per_page).
offset((pagination.page - 1) * pagination.items_per_page).
order(:id)

Related

Is it okay to do binary search on an indexed column to get data from a non-indexed column?

I have a large table users(id, inserttime, ...), with index only on id. I would like to find list of users who were inserted between a given start_date and finish_date range.
User.where(inserttime: start_date..finish_date).find_each
^This leads to a search which takes a lot of time, since the inserttime column is not indexed.
The solution which I came up with is to do find user.id for start_date and finish_date separately by doing a binary search twice on the table using the indexed id column.
Then do this to get all the users between start_id and finish_id:
User.where(id: start_id..finish_id).find_each
The binary search function I am using is something like this:
def find_user_id_by_date(date)
low = User.select(:id, :inserttime).first
high = User.select(:id, :inserttime).last
low_id = low.id
high_id = high.id
low_date = low.inserttime
high_date = high.inserttime
while(low_id <= high_id)
mid_id = low_id + ((high_id - low_id) / 2);
mid = User.select(:id, :inserttime).find_by(id: mid_id)
# sometimes there can be missing users. Ex: [1,2,8,9,10,16,17,..]
while mid.nil?
mid_id = mid_id + 1
mid = User.select(:id, :inserttime).find_by(id: mid_id)
end
if (mid.inserttime < date)
low_id = mid.id + 1
elsif (mid.inserttime > date)
high_id = mid.id - 1
else
return mid.id
end
end
# when date = start_date
return (low_id < high_id) ? low_id + 1 : high_id + 1
# when date = finish_date
return (low_id < high_id) ? low_id : high_id + 1
end
I am not sure if what I am doing is the right way to deal with this problem or even if my binary search function covers all the cases.
I think the best solution would be to add an index on inserttime column but that is sadly not possible.
This might not be the best way to do it, but if the IDs are numeric and sequential you could write a query to find the users in between the minimum and maximum user ID:
SELECT id
FROM users
WHERE id BETWEEN [low_id_here] AND [high_id_here];
In ActiveRecord:
low = User.select(:id, :inserttime).first
high = User.select(:id, :inserttime).last
low_id = low.id
high_id = high.id
User.where('id BETWEEN ? AND ?', low_id, high_id)

Why is this query giving me a syntax error?

Everytime I try to compile this code on DBeaver, it gives me a syntax error right where the first AND statement is located. I don't know how to solve it.
SELECT idintervento as _IDInterventoRif, idcdc, idcartella,
(SELECT COUNT(DISTINCT idintervento) as nInt
FROM cch.pats_cch_interventi i
WHERE (idtiporecord in('UTI','INT'))
AND i.idcdc = inter.idcdc
AND i.idcartella = inter.idcartella
AND i.utiingressodata < inter.utiingressodata
AND i.idintervento <> inter.idintervento
) as _IDInterventoTipoN
FROM cch.pats_cch_interventi inter
WHERE (idtiporecord in('INT') OR idtiporecord IS NULL)
AND idintervento <> ?
AND idcdc = ?
AND idcartella = ?
AND (dataintervento < ?
OR dataintervento = ? AND idintervento < ?)
ORDER BY idcdc, idcartella, utiingressodata desc;

SQL Doctrine year's eve

that's is strange, maybe is my fault. Today I launch my test and 2 of they faults (yesterday dont). This test use a control date of some bookings, and I presume the problem is that today is 31/12. I'll show you the code:
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT b
FROM AppBundle:Booking b
WHERE b.bookingDate >= CURRENT_DATE()
AND b.bookingDate <= CURRENT_DATE()+1
ORDER ASC b.bookingDate'
)
return $booking = $query->getResult();
That way is the only way i found to check that the booking have a date at today. Is possible that this fault becouse today is 31/12? Do you have some solution?
sorry for bad english, thanks.
You can calculate the dates by Php:
$today = new DateTime('now');
$tomorrow = new DateTime('tomorrow');
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT b
FROM AppBundle:Booking b
WHERE b.bookingDate >= :today
AND b.bookingDate < :tomorrow
ORDER BY b.bookingDate ASC'
);
return $query->setParameters(array(
'today' => $now->format('Y-m-d'),
'tomorrow' => $tomorrow->format('Y-m-d')
))->getResult();
You can avoid var $booking and return result directly.
Also you have missed ; at the end of createQuery and ORDER is ORDER BY.
Also beware with namespaces, maybe you must to use new \DateTime('now');
CURRENT_DATE()+1
returns
20161232
which seems to be pretty wrong.
A way to make it works as you want it to would be this:
DATE_ADD(CURRENT_DATE(), INTERVAL 1 DAY);
which returns
2017-01-01

rails make query better

In my table schema
I have “listeners” table connect to “devices” table (one to many)
Now I need to search on listener table but with the devices.id field
My problem is that the 2 table should have many of records (more than 1,000,000) and I afraid that my query will crash the application
My problem is on the in select
I want to know what can I do to make this query better
My full query on gist https://gist.github.com/anonymous/8623345
limit = 10
#listeners = Listener.all
#total_count = #listeners.count
#sEcho = params[:sEcho]
**#listeners =#listeners.where(" (listeners.name like ?) or (listeners.email like ?) or (countries.name like ?) or (listeners.city like ?) or (listeners.id in (select listener_id from devices where id = ?))", "%#{params[:sSearch]}%", "%#{params[:sSearch]}%" , "%#{params[:sSearch]}%","%#{params[:sSearch]}%","#{params[:sSearch]}") if params[:sSearch].present?**
order_by ="listeners.id asc"
#listeners = #listeners.includes(:devices)
#listeners = #listeners.includes(:country)
#count = #listeners.count
#listeners = #listeners.order(order_by)
#listeners = #listeners.limit(limit).offset(params[:iDisplayStart])
You need to use kaminari or will paginate for pagination and you are making this more complex this is the shortest form i think so:
Listener.includes(:devices, country).where(" (listeners.name like ?) or (listeners.email like ?) or (countries.name like ?) or (listeners.city like ?) or (listeners.id in (select listener_id from devices where id = ?))", "%#{params[:sSearch]}%", "%#{params[:sSearch]}%" , "%#{params[:sSearch]}%","%#{params[:sSearch]}%","#{params[:sSearch]}").limit(limit).offset(params[:iDisplayStart]||1).order(:id)

How to write a Linq query equivalent to this SQL

I'm trying to figure out how to write a linq query that will return equivalent results to the sql query below. The problem I'm having has to do with the two select count queries included in the select list of the main query. I need to get counts of two different types of records from the PaymentHistory table for the last year. Can the equivalent of this be written using linq? Preferrably using lambda syntax.
select ieinum, serviceaddrkey,
(select count(*) from PaymentHistory where serviceaddrid = serviceaddrkey
and PostDate >= DateAdd(year, -1 , GetDate())
and SetID = 100) as ReturnedFees,
(select count(*) from PaymentHistory where serviceaddrid = serviceaddrkey
and PostDate >= DateAdd(year, -1 , GetDate())
and SetID = 101) as CorrectedReturnedFees
from Serviceaddr
Any help will be great.
Perhaps something like this:
from s in Serviceaddr
let ph = PaymentHistory.Where(p => s.serviceaddrkey == p.serviceaddrkey &&
p.PostDate >= DateTime.Now.AddYears(-1))
select new
{
s.ieinum,
s.serviceaddrkey,
ReturnedFees = ph.Count(p => p.SetID == 100),
CorrectedReturnedFees = ph.Count(p => p.SetID == 101)
}