Select where MAX(DATE) between X and X - sql

I'm trying to select only patients, who had their LAST appointment in the year 2015. The ideal thing would look like this:
Select person.name, person.surname
inner join patient on patient.pat_id=person.per_id
inner join appointment on appointment.pat_id=patient.pat_id
where MAX(appointment.date) between '31.12.2014'and '01.01.2016'
But of MAX isn't allowed in WHERE clause. What's the best workaround?

You are close:
Select p.name, p.surname
from person p inner join
patient pa
on pa.pat_id = p.per_id join
appointment a
on a.pat_id = pa.pat_id
group by p.per_id, p.name, p.surname
having max(a.date) >= date '2015-01-01' and
max(a.date) < date '2016-01-01';
Notes:
You need group by.
Note that I'm including per_id in the group by, because different people can have the same name.
Date constants can be introduced with the date keyword.
Do not use between with dates; this is particularly true in Oracle where a date column can have a time component.

Related

Display Value from Sub Query in SELECT

I need to display the value MIN(pa.DataAdjudicacao) in the SELECT.
And how can I replace this Subquery for a Join?
How can I do it?
As far as I have searched I haven't found any way to do it.
Please help.
SELECT p.id, p.referencia
FROM PCTSproposta p
WHERE p.ID in (SELECT pa.Proposta
FROM PropostaAdjudicada pa
WHERE pa.Proposta = p.ID
GROUP BY pa.Proposta
HAVING MIN(pa.DataAdjudicacao) >= '2020-10-01' And MIN(pa.DataAdjudicacao) <= '2020-10-31')
Your query is overcomplicated.
You use subquery to filter the data by IN predicate, but select the same ID that you've passed into filter of subquery. So your IN looks like EXISTS.
But if you need that min, you should use JOIN:
SELECT p.id,
p.referencia,
pa2.DataAdjudicacao
FROM PCTSproposta p
JOIN (
SELECT pa.Proposta,
MIN(pa.DataAdjudicacao) as DataAdjudicacao
FROM PropostaAdjudicada pa
GROUP BY pa.Proposta
HAVING MIN(pa.DataAdjudicacao) BETWEEN date '2020-10-01'AND date '2020-10-31'
) pa2
on pa2.Proposta = p.ID
Depending on roles of the Id column (if it is primary key of PCTSproposta), data integrity and cardinality the subquery can be merged into main query:
SELECT p.id,
p.referencia,
MIN(pa.DataAdjudicacao) as DataAdjudicacao
FROM PCTSproposta p
JOIN PropostaAdjudicada pa
on pa2.Proposta = p.ID
GROUP BY p.id,
p.referencia
HAVING MIN(pa.DataAdjudicacao) BETWEEN date '2020-10-01'AND date '2020-10-31'
A lateral join comes to mind:
SELECT p.id, p.referencia, pa.*
FROM PCTSproposta p
CROSS APPLY (
SELECT MIN(pa.DataAdjudicacao) minDataAdjudicacao
FROM PropostaAdjudicada pa
WHERE pa.Proposta = p.ID
) pa
WHERE pa.minDataAdjudicacao >= '20201001' AND pa.minDataAdjudicacao < '20201101'

How to select customers that didn't place an order in the last 7 days

I am trying to find out the customers that didn't place an order in the last seven days. Basically I have 3 tables: customers, orders and help_desk_agents.
I'm trying to figure out the best way to get this information.
The SQL bellow retrieves the customers info, the help desk agent 111 and the last date of the orders of each customer:
SELECT DISTINCT customers.customer_id,
customers.customer_name,
agents.help_desk_agent,
Max(orders.order_date)
FROM customers
LEFT JOIN (SELECT DISTINCT customers.customer_id,
orders.order_date
FROM orders
GROUP BY 1,
2) orders2
ON customers.customer_id = orders2.customer_id
LEFT JOIN help_desk_agents
ON customers.help_desk_agent_id =
help_desk_agents.help_desk_agent_id
WHERE customer.help_desk_agent_id = 111
GROUP BY 1,
2,
3
I would like like somehow to filter the customers that didn't place an order in the last seven days.
Try adding this at the and of your query :
having max(orders.order_date) < dateadd(day, -7, getdate())
You can try a
Datediff(dd,<datecolumn>,getdate())
and use
>= 7
as a condition.
The query that you want should look like this:
SELECT c.customer_id, c.customer_name, a.help_desk_agent,
Max(orders.order_date)
FROM customers c JOIN
(SELECT o.customer_id, MAX(o.order_date) as max_order_date
FROM orders o
GROUP BY o.customer_id
) o
ON c.customer_id = o.customer_id
WHERE c.help_desk_agent_id = 111 AND
o.max_order_date < dateadd(day, -7, getdate());
Your query has multiple issues:
The alias customers.customer_id is not understood in the subquery.
The select distinct is unnecessary.
LEFT JOIN is unnecessary because presumably customers have at least one order and you require a match to the agent table.
You don't need the agent table, because the information you want is in the customer table.

SQL join/sum assistance, need to combine the 2 queries inside

Here's a fiddle:
http://sqlfiddle.com/#!2/7f479a/2
What I am trying to achieve is the Inventory from this query:
SELECT
di.distributors_inventory_stock,
op.op_products_id,
p.products_mfr_part_number,
op.op_products_name,
p.products_brand,
op.op_products_qty,
SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o
ON op.op_order_id = o.orders_id
JOIN products p
ON p.products_id = op.op_products_id
JOIN distributors_inventory di
ON di.distributors_inventory_product_ID = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
With the TotalSold and product list from this query:
SELECT
op.op_products_id,
p.products_mfr_part_number,
op.op_products_name,
p.products_brand,
SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o ON op.op_order_id = o.orders_id
JOIN products p ON p.products_id = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
Because the distributors data is driving the list of products, that needs to be the first table queried, then left join to the other tables, putting all conditions on the other tables in the join condition:
SELECT
di.distributors_inventory_stock stock,
di.distributors_inventory_product_ID product_id,
p.products_mfr_part_number mfr_part_number,
op.op_products_name order_product_name,
p.products_brand brand,
p.products_supplier_name supplier_name,
SUM(op.op_products_qty) / count(o.orders_id) AS TotalSold
FROM distributors_inventory di
LEFT JOIN orders_products op ON di.distributors_inventory_product_ID = op.op_products_id
LEFT JOIN orders o ON op.op_order_id= o.orders_id
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
LEFT JOIN products p ON p.products_id = di.distributors_inventory_product_ID
WHERE di.distributors_inventory_distributor_ID = '90'
GROUP BY 1, 2, 3, 4, 5, 6
ORDER BY TotalSold DESC
See SQLFiddle
The key points here are:
select first from the table that controls what rows are returned
left join to the sales tables
move the conditions on left joined tables, specifically the date range, from the where clause into the join condition
removed column that was both summed and selected (makes no sense)
corrected group by clause problem (you must list all non-aggregated selected columns for the grouping to work as you expect)
join to product table directly from distributors table so product info is available for products without sales in the period
give columns friendlier names
added supplier product name (because we can)
modified sum() due to account for multiple joined rows from the order table (from comment)
Of all the tips, the third is the most critical. Because left joined rows have nulls in their columns, putting a condition on them in the where clause will result in only rows that actually join being returned - effectively making the joins inner joins. Conditions in the join condition still restrict rows returned, while allowing the join to be an outer join
Your First Query is Good ... just add LEFT OUTER JOIN To distributors_inventory table.. and you should get your expected result set.
Modified Query: (check the comment in below query for modification)
SELECT di.distributors_inventory_stock, op.op_products_id, p.products_mfr_part_number, op.op_products_name, p.products_brand, op.op_products_qty, SUM(op.op_products_qty) AS TotalSold
FROM orders_products op
JOIN orders o ON op.op_order_id = o.orders_id
JOIN products p ON p.products_id = op.op_products_id
-- Comment: Modification in below Line, inner join changed to left outer join)
LEFT OUTER JOIN distributors_inventory di ON di.distributors_inventory_product_ID = op.op_products_id
WHERE o.orders_distributor_id = '90'
AND o.orders_date_purchased BETWEEN '2014-06-01 00:00:00' AND '2014-08-01 23:59:59'
GROUP BY op.op_products_id
ORDER BY TotalSold DESC
You will get null values under distributors_inventory_stock column if corresponding row is not found in this table.. if you want zero instead of null then change it as below in select clause
Select di.distributors_inventory_stock
To
Select ifnull(di.distributors_inventory_stock,0) AS distributors_inventory_stock

Oracle SQL Missing Keyword

I'm trying to display the most popular product (format_id) in a given month (JAN-14) and group them by a count of each format_id.
Here is my Query :
select PRODUCT, AMOUNT
from (Select Order_108681091.Order_Date, order_line_108681091.Format_id as Product,
COUNT(*) AS AMOUNT FROM order_line_108681091
Inner Join order_108681091
On order_108681091.order_id = order_line_108681091.order_id
Where order_108681091.Order_Date like '%JAN-14%'
group by Format_id
order by AMOUNT desc);
How can i do this ?
You said you have the condition, so let's start with adding it.
There is no need to ORDER BY, so remove it.
Remove Order_date from the subquery SELECT
Use aliases
The subquery itself would be enough.
SELECT l.Format_id as PRODUCT,
COUNT(*) AS AMOUNT
FROM order_line_108681091 l
INNER JOIN order_108681091 o
ON o.order_id = l.order_id
WHERE o.Order_Date LIKE '%JAN-14%'
GROUP BY
l.Format_id;
You have to specify the inner join:
inner join order_108681091 ON order_1078681091.ID = order_line_018681091.ID
or something like that. Also your where clause probably wont work unless you're storing that date as a string and not a datetime datatype.
You don't need the subquery at all. And, I'm very uncomfortable using like on a date directly. Explicitly convert the date to a string:
select ol.Format_id as Product, COUNT(*) AS AMOUNT
from order_line_108681091 ol Inner Join
order_108681091 o
ON o.order_id = ol.order_id
where to_char(o.Order_Date, 'MMM-YYYY') = 'JAN-2014'
group by ol.Format_id
order by count(*) desc;
Actually, if you have in index on OrderDate, you can use the following (to take advantage of the index):
select ol.Format_id as Product, COUNT(*) AS AMOUNT
from order_line_108681091 ol Inner Join
order_108681091 o
ON o.order_id = ol.order_id
where o.Order_Date >= to_date('2014-01-01', 'YYYY-MM-DD') and
o.Order_Date < to_date('2014-02-01', 'YYYY-MM-DD')
group by ol.Format_id
order by count(*) desc;
Moving the function from the column to the constant allows the use of an index on the column.

Stuck on what is a simple query

If i have 2 tables one for people and one for holidays. Everytime someone goes on holiday the date gets entered in the holiday table. How would I query this so it shows the person name from the persons table if they have been on more then say 2 holidays between 1st of jan 2010 and the 6th of june 2010? This seems simple but I cant seem to do it.
If all you want is the list of names of people taking 2 or more days between those two dates:
SELECT people.name
FROM people
WHERE EXISTS (
SELECT count(*)
FROM days_taken
WHERE people.person_id=days_taken.person_id AND
days_taken.vacation_date BETWEEN date1 AND date2
HAVING count(*)>=2
)
If you want the name and the number of days:
SELECT people.name,count(*)
FROM people JOIN days_taken ON people.person_id=days_taken.person_id
WHERE days_taken.vacation_date BETWEEN date1 AND date2
GROUP BY people.name
HAVING count(*)>=2
SELECT people.name, COUNT(*) c
FROM people INNER JOIN holidays
ON people.user_id = holidays.user_id
WHERE holidays.departure_date BETWEEN date1 AND date2
GROUP BY people.name
HAVING c > 2
SELECT p1.name, p2.num_holidays
FROM people p1
INNER JOIN
(
SELECT people.user_id, COUNT(*) as num_holidays
FROM people
INNER JOIN holidays ON (people.user_id = holidays.user_id)
WHERE holidays.departure_date BETWEEN date1 AND date2
GROUP BY people.user_id
HAVING COUNT(*) > 2
)p2 ON (p2.user_id = p1.user_id)
For SQL Server 2005 onwards, you can use the analytical function Count() OVER
select p.*, h.C
from person p inner join
(
select distinct person_id, C = COUNT(*) over (partition by person_id)
from holiday
where holiday_date between '20100101' and '20100606'
) h on h.person_id = p.person_id and h.C >= 2