sqlite select query - sql

I am working in Android with SQLite.
My db has 2 tables:
Table 1: cars
_id(int,pri-key)
reg(text)
type(text)
Table 2: jobs
_id(int,foreign-key)
date(date)
I need a sqlite statment which will get me all cars which have NOT had a job in the past 3 weeks. Iam actually porting the app from c#, and the statment I use for this (in MySQL) is
SELECT c.id, c.reg, c.type FROM buses c WHERE NOT EXISTS (SELECT NULL FROM jobs j WHERE j.id = c.id AND j.date >= CURRENT_DATE - INTERVAL 21 DAY
But the SqliteDataBase Object I am working with in android takes a different format, How would I run this query?
Thanks in advance.

I would try something like this:
SELECT * from cars A LEFT OUTER JOIN jobs B on A._id = B._id WHERE B._id IS NULL OR B.date < date('now', '-21 days');
The LEFT OUTER JOIN, ensures all values from the cars table are shown in the output (including the ones that don't match the join criteria - i.e. ones that don't have an entry in the jobs table). The WHERE criteria, filters for either, ones that don't have an entry in the jobs table (B._id IS NULL) or ones that are more than 21 days old B.date < date('now', '-21 days')
Of course I am assuming, there will be only 1 entry on the Jobs table for each car. If there will be more, you probably want to use MAX to get the latest date.
WORKING SOLUTION: SELECT * from cars A LEFT OUTER JOIN jobs B on A._id = B._id GROUP BY A._id HAVING B._id IS NULL OR MAX(B.date) < date('now', '-21 days');

Related

How to make query faster

Here is my code:
SELECT m.id, (SELECT count(r.id)
FROM reports as r
WHERE r.date BETWEEN NOW()-INTERVAL '7 days' AND NOW()
and r.decision = 'ACCEPTED'
and r.customer_id in (SELECT c.id
FROM customers as c
WHERE c.manager_id = m.id)) as count
FROM managers as m;
Where tables are managers, customers and reports. I want to SELECT, for every manager, number of reports all of his customers have made (with selected properties). Problem with this code is that it takes too much time and somehow cannot find a better way of building this query. Do you have any idea?
I think , a standard inner join among those three table with indexes defined on reports.date, reports.customer_id and customers.manager_id columns as mentioned on the comment( moreover considering managers.id and customer.id columns as expected to be primary keys which already have unique indexes on them ) will suffice for performance point of view :
SELECT m.id, count(r.id)
FROM customers c
JOIN managers m ON r.customer_id = c.id
JOIN reports r ON m.id = c.manager_id
WHERE r.date BETWEEN NOW() - INTERVAL '7 days' AND NOW()
AND r.decision = 'ACCEPTED'
GROUP BY m.id

Finding days when users haven't created any entries

I've 2 tables: users and time_entries, time entries has a foreign key to the users table. Users may create time entries with some time amount in it. I want to write a query which could return summarized amounts of time in arbitrary dates range grouped by user and date - it's easy but I need to include also days when nobody entered any time_entry. I've tried to create an additional table called calendar with dates and left join time_entries to it but I couldn't retrieve a list of users that haven't entered any time_entry. Here is my query:
SELECT te.date, SUM(te.amount), user_name
FROM calendar c
LEFT JOIN time_entries te on c.date = te.date
RIGHT JOIN asp_net_users anu on te.user_id = anu.id
GROUP BY user_name, te.date
If you just want the days no user made any entry. you can use NOT EXISTS and a correlated subquery.
SELECT c.date
FROM calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.date = c.date);
If you want all users along with the days they haven't made any entry cross join the users and the days and then also use a NOT EXISTS.
SELECT anu.user_name,
c.date
FROM asp_net_users anu
CROSS JOIN calendar c
WHERE NOT EXISTS (SELECT *
FROM time_entries te
WHERE te.user_id = anu.id
AND te.date = c.date);
Thanks to sticky bit examples I was able to write the following query which solves my problem:
SELECT c.date, a.id, COALESCE(sum(te.amount), 0)
FROM asp_net_users a
CROSS JOIN (SELECT *
FROM calendar
WHERE date BETWEEN '2019-10-01 00:00:00'::timestamp AND '2019-10-31 00:00:00'::timestamp) c
LEFT JOIN time_entries te on a.id = te.user_id AND c.date = te.date
WHERE a.department_guid = '95b7538d-3830-48d7-ba06-ad7c51a57191'
GROUP BY c.date, a.id
ORDER BY c.date

Write SQL query that returns results for each sorted value in main column

I'm working on a project related with my college and I have the following tables:
user - table with information about registered students
payment - table with information about payments
lesson - table with list of lessons.
Possible statuses of lessons:
CONFIRMED - lesson happened successfully
SCHEDULED - for future
lessons
CANCELED - for lessons that were canceled
Each lesson appears in table only one time. Lesson status is being updated.
I need to write a SQL query that returns for each country:
number of registered users
% of users, who made their first payment in 3 days after registration
% of users, who made their first payment in 3 days after registration and had 2 confirmed
lessons in 7 days after registration
But aside from using DISTINCT to sort the table with non repeated elements, I'm stuck with the subqueries required to filter the information using each country as reference.
Perhaps you need to adapt this to your SQL dialect (mainly adding intervals on timestamps). I guess the rest should work (not tested anyways).
WITH pay3day AS (
SELECT u.id
FROM user u
INNER JOIN payment p ON p.user_id = u.id
WHERE p.datetime <= u.date_joined + INTERVAL '3 days'
GROUP BY u.id
), less2in7 AS (
SELECT u.id
FROM user u
INNER JOIN lesson l ON l.user_id = u.id
WHERE l.status = 'CONFIRMED'
AND l.datetime <= u.date_joined + INTERVAL '7 days'
GROUP BY u.id
HAVING COUNT(*) > 0
)
SELECT
COUNT(*) AS a,
(COUNT(p.id) * 100.0) / COUNT(*) AS b,
(COUNT(p.id + l.id) * 100.0) / COUNT(*) AS c
FROM user u
LEFT JOIN pay3day p ON p.id = u.id
LEFT JOIN less2in7 l ON l.id = u.id
GROUP BY u.country_code

Teradata recurring payment patterns

I am using a customer period level data to find recurring patterns of the transactions they have done.
What I want is to find if the customer is doing recurring payments of the amounts +-10% either weekly, fortnightly or monthly.
I am restricted to Teradata for this, so I am using self join with the same table defining join on accounts and channel through which the transaction is going. As the channel can vary, there can be multiple transactions in a day so that filter on join is required. I am also giving a loose window as people can pay a day after or before also.
The query I am using currently is like this:
`
create multiset table weekly as
(
sel a.*, b.*, c.*
from table1 a
join table1 b
on a.account = b.account and a.channel = b.channel
join table1 c
on a.account = c.account and a.channel = c.channel
where c.date between b.date + 6 and b.date + 9 and b.amount between 0.9*(c.amount) and 1.1*(c.amount)
and b.date between a.date + 6 and a.date + 9 and a.amount between 0.9*(b.amount) and 1.1*(b.amount)
)
with data
primary index(account)
'
Is there any other efficient way of doing is becuase I am able to max join 3 self joins and I need more than 3 recurrence to confirm its weekly? I can migrate this data to R and then build some algo on it but data size is around 40 million.

Match two tables based on minimum dates efficiently

I have two tables one which contains quarterly data and one which contains daily data. I would like to join the two tables such that for each day in the daily data the quarterly data for that quarterly is selected and returned daily. I am working with Postgres 9.3.
The current query is as follows:
select
a.ID,
a.datadate,
b.*,
case when a.datadate = b.rdq then 1 else 0 end as VALID
from proj_data a, proj_rat b
where a.id = b.id
and b.rdq = (select min(rdq)
from proj_rat c
where a.id = c.id and a.datadate >= c.rdq);
But it is excruciatingly slow and I need to do this for several thousand IDs. Can anyone suggest a more efficient solution?
This eliminates the need for a subquery in the where clause
select
ID,
a.datadate,
b.*,
(a.datadate = b.rdq)::integer as VALID
from
proj_data a
inner join
(
select distinct on (id, rdq) *
from project_rat
order by id, rdq
) b using(id)
where a.datadate >= b.rdq;