MySql get rows between date values - sql

I'm doing the following query and getting all rows returned, regardless of date:
SELECT DISTINCT p.name, p.category, u.f_name, t.name
FROM (
prizes p, tags t, results r
)
LEFT JOIN
users u
ON (r.user_id = u.id)
LEFT JOIN
f_tag_lookup tl
ON (tl.tag_id = t.id)
WHERE r.tag_id = t.id
AND r.date BETWEEN concat(date_format(LAST_DAY(now() - interval 1 month), '%Y-%m-'),'01') AND last_day(NOW() - INTERVAL 1 MONTH)
r.date is a datetime field. there are three rows with dates from last month and three rows with dates from this month but I'm getting all rows back?
Would appreciate any pointers. I want to return results between the first and last day of last month i.e. all results for July.
thanks,

Could you not just use the month() date function ?
e.g. use month(date) = month(now()) as the filter.
You would need to also match on year(date) = year(now()), if you had data from more than one year.
I'm not sure of the performance, but it seems more readable to me to express it that way.
SELECT DISTINCT p.name, p.category, u.f_name, t.name
FROM (prizes p, tags t, results r)
LEFT JOIN users u ON (r.user_id = u.id)
LEFT JOIN f_tag_lookup tl ON (tl.tag_id = t.id)
WHERE r.tag_id = t.id AND
month(r.date) = month(now()) AND
year(r.date) = year(now())

figured it out. The SQL regarding dates was fine but the JOINS were wrong. Thanks for the replies.

Related

Returning not all rows with query

Schema:
I've a task:
Print the names of the most active users who gave more than 100 responses in the first month after registration (including the day of registration). Questions asked by users, do not count. For each username, print the number of unique user_id values. Sort the result by the field with the names in lexicographic order.
SELECT
u.display_name,
COUNT(p.user_id) AS answers_count
FROM stackoverflow.users u
JOIN stackoverflow.posts p ON u.id = p.user_id
JOIN stackoverflow.post_types pt ON p.post_type_id = pt.id
WHERE pt.type = 'Answer'
AND p.creation_date <= u.creation_date + INTERVAL '1 month'
GROUP BY u.display_name
HAVING COUNT(p.id) > 100
ORDER BY u.display_name
But after comparing result with validator it says that I've too few rows. Seems like my WHERE condition is not properly set?

How to only include full months - Postgres

I have the following SQL query:
SELECT
SUM(amount)
FROM
(SELECT
l.human_readable_id,
DATE_TRUNC('day', c.created_date)::TIMESTAMP AS Date,
(ROUND(c.amount/100.00, 2))::DOUBLE PRECISION AS amount,
(ROUND(c.amount/100.00, 2)*0.04)::DOUBLE PRECISION AS Repayment,
c.currency,
c.payment_type,
c.status,
c.payment_id
FROM
loan_applications AS l
LEFT JOIN
merchants AS m ON l.merchant_id = m.id
LEFT JOIN
codat_companies AS cc ON m.id = cc.merchant_id
LEFT JOIN
codat_commerce_payments AS c ON cc.id = c.codat_company_id
WHERE
amount IS NOT NULL) AS subquery
GROUP BY
date
And get the sum of every month. Based on this, I can calculate the average. Is it possible to only include full months? For instance this is data from 1st of May 2021 until yesterday. But including this month would have a negative impact on the overall monthly average.
Thanks in advance
You can add a condition in the WHERE clause :
WHERE amount IS NOT NULL
AND c.created_date < date_trunc('month', Now())

SQL subquery: How to grap the record with both the min(date) and min(hour)

I have a table from which I want to grap the first record that gives a passage IN ('B410','B420','C430','C440'). So, I suppose this means looking for the record with the oldest date and within that oldest date the record with the oldest time stamp.
I wrote a query with 2 nested subqueries, but that query does not do the trick (it gives me an empty result). What am I doing wrong? The strange thing is that when changing the syntax to find the max(fromdate) and max(hour), I do find the record with timstamp 22:18. But I do not find the record with timestamp 16:40 with min(fromdate) and min(hour).
select v.id,
h.fromdate,
h.hour,
h.department,
h.room
from visit v
inner join visit_hist h
on v.id = h.id
where v.id in ('10251183')
and h.room in ('B410','B420','C430','C440')
and h.fromdate =
(select min(fromdate)
from visit_hist
where (id= h.id
and h.hour =
(select min(hour) from visit_hist where id= h.id and h.date = date
))
)
order by v.date,
v.visit_id;
Can someone help me out here?
Just use window functions:
select . . .
from visit v inner join
(select vh.*, row_number() over (partition by vh.id order by date asc, hour asc) as seqnum
from visit_hist vh
where vh.room in ('B410', 'B420', 'C430', 'C440')
) vh
on v.id = vh.id
where v.id in (10251183) and seqnum = 1;
Note: This gives the ids with the first room in the list. If you want the ids whose first room is in the list, then move the condition on vh.room to the outer query.

How do combine a count in a particular select statement?

On our postgresql database we currently have 2 tables called Users and Bookings.
We are currently trying to know on the users that made a booking yesterday how many bookings they did over time.
Here is the query we have for the moment:
SELECT "domain".users.email, COUNT("domain".bookings."id")
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
If we have the date the booking was created (field "domain.bookings.created_at) with filter yesterday we only get the data from yesterday.
Is there a way to see on who bought yesterday how many bookings they did overtime?
Thank you!
Luca
TRY THIS
SELECT "domain".users.email, COUNT(CASE WHEN bookingdate = current_date - 1 THEN 1 END ) AS TOTAL_BOOKING
FROM "domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY "domain".users.email
This is what you are looking for:
SELECT u.id, u.email, count(b.id) AS "Total Bookings"
FROM "domain".bookings as b
JOIN "domain".users as U ON u.id = b.user_id
WHERE EXISTS (SELECT 1
FROM "domain".bookings b2
WHERE b2.created_at = current_date - 1
AND b2.user_id = b.user_id)
GROUP BY u.email;
The exists condition will only return rows from the bookings table for users that booked something yesterday.
Unrelated, but: using a keyword like domain that requires double quotes for identifiers is not such a good idea. It would save you some trouble in the long run if you found a different name
Thank you for answering. I was probably not precise enough!
Basically if I use the following query:
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
GROUP BY
"domain".users.email
I get the number of bookings made by a user.
BUT
I want to get the following: the number of bookings made all time by the users that bought yesterday.
I tried this
SELECT
"domain".users.email,
COUNT( "domain".bookings."id") AS "Total Bookings"
FROM
"domain".bookings
INNER JOIN "domain".users ON "domain".users."id" = "domain".bookings.user_id
WHERE
"domain".bookings.created_at = 'yesterday'
GROUP BY
"domain".users.email
But I am getting no responses...
Thank you for help!
Luca

Left and right joining in a query

A friend asked me for help on building a query that would show how many pieces of each model were sold on each day of the month, showing zeros when no pieces were sold for a particular model on a particular day, even if no items of any model are sold on that day. I came up with the query below, but it isn't working as expected. I'm only getting records for the models that have been sold, and I don't know why.
select days_of_months.`Date`,
m.NAME as "Model",
count(t.ID) as "Count"
from MODEL m
left join APPLIANCE_UNIT a on (m.ID = a.MODEL_FK and a.NUMBER_OF_UNITS > 0)
left join NEW_TICKET t on (a.NEW_TICKET_FK = t.ID and t.TYPE = 'SALES'
and t.SALES_ORDER_FK is not null)
right join (select date(concat(2009,'-',temp_months.id,'-',temp_days.id)) as "Date"
from temp_months
inner join temp_days on temp_days.id <= temp_months.last_day
where temp_months.id = 3 -- March
) days_of_months on date(t.CREATION_DATE_TIME) =
date(days_of_months.`Date`)
group by days_of_months.`Date`,
m.ID, m.NAME
I had created the temporary tables temp_months and temp_days in order to get all the days for any month. I am using MySQL 5.1, but I am trying to make the query ANSI-compliant.
You should CROSS JOIN your dates and models so that you have exactly one record for each day-model pair no matter what, and then LEFT JOIN other tables:
SELECT date, name, COUNT(t.id)
FROM (
SELECT ...
) AS days_of_months
CROSS JOIN
model m
LEFT JOIN
APPLIANCE_UNIT a
ON a.MODEL_FK = m.id
AND a.NUMBER_OF_UNITS > 0
LEFT JOIN
NEW_TICKET t
ON t.id = a.NEW_TICKET_FK
AND t.TYPE = 'SALES'
AND t.SALES_ORDER_FK IS NOT NULL
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
GROUP BY
date, name
The way you do it now you get NULL's in model_id for the days you have no sales, and they are grouped together.
Note the JOIN condition:
AND t.CREATION_DATE_TIME >= days_of_months.`Date`
AND t.CREATION_DATE_TIME < days_of_months.`Date` + INTERVAL 1 DAY
instead of
DATE(t.CREATION_DATE_TIME) = DATE(days_of_months.`Date`)
This will help make your query sargable (optimized by indexes)
You need to use outer joins, as they do not require each record in the two joined tables to have a matching record.
http://dev.mysql.com/doc/refman/5.1/en/join.html
You're looking for an OUTER join. A left outer join creates a result set with a record from the left side of the join even if the right side does not have a record to be joined with. A right outer join does the same on the opposite direction, creates a record for the right side table even if the left side does not have a corresponding record. Any column projected from the table that does not have a record will have a NULL value in the join result.