Multiple Counts Over Multiple Dates - sql

I am essentially doing the following query (edited):
Select count(orders)
From Orders_Table
Where Order_Open_Date<=##/##/####
and Order_Close_Date>=##/##/####
Where the ##/##/##### is the same date. So in essence the number of 'open' orders for any given day. However I am wanting this same count for every single day for a year and don't want to write a separate query for each day for the whole year. I'm sorry this is probably really simple but I am new to SQL and I guess I don't know how to search for an answer to this question since my searches have come up with nothing. Thanks for any help you can offer.

why not
select Order_Date, count(orders) from Orders_Table group by Order_Date
and for last year
select Order_Date, count(orders) from Orders_Table where Order_Date > DATE_SUB(CURDATE(), INTERVAL 1 YEAR) group by Order_Date;

SELECT CONVERT(VARCHAR, Order_Date, 110), count(orders)
FROM Orders_Table
WHERE Order_Date = BETWEEN #A AND #B
GROUP BY CONVERT(VARCHAR, Order_Date, 110)

If you want to have every day of the year, including those with no orders, you will need to generate a temporary table or similar containing every date in the range and left/right join it to the Orders_Table data. This depends upon which RDBMS you're using. In SQL Server I have done this using a user defined function which returns a table variable.

Related

Data value on a given date

This time I have a table on a PostgreSQL database that contains the employee name, the date that he started working and the date that he leaves the company, in the cases of the employee still remains in the company, this field has null value.
Knowing this, I would like to know how many people was working on a predetermined date, ex:
I would like to know how many people works on the company in January 2021.
I don't know where to start, in some attempts I got the number of hires and layoffs per month, but I need to show this accumulated value per month, in another column.
I hope I made myself understood, I'll leave the last SQL I got here.
select reference, sum(hires) from
(
select
date_trunc('month', date_hires) as reference,
count(*) as hires
from
ponto_mais_relatorio_colaboradores
group by
date_hires
union all
select
date_trunc('month', date_layoff) as reference,
count(*)*-1 as layoffs
from
ponto_mais_relatorio_colaboradores
group by
date_layoff
) as reference
join calendar_aux on calendar_aux.ano_mes = reference
group by reference
order by reference
Break the requirement down. The question: how many are employed on any given date? That would include all hired before that date and do not have a layoff date plus all hired before with a layoff date later then the date your interested period. I.e you are interested in Jan so you still want to count an employee with a layoff date in Feb. With that in place convert into SQL. The preceding is available from select comparing dates. other issue is that Jan is not a date, it is a range of dates, so you need each date. You can use generate series to create each day in Jan. Then Join the generated dates with and selection from your table. Resulting query:
with jan_dates( jdate ) as
( select generate_series( date '2021-01-01'
, date '2021-01-31'
, interval '1' day
)::date
)
select jdate "Date", count(*) "Employees"
from jan_dates j
join employees e
on ( e.date_hires <= j.jdate
and ( e.date_layoff is null
or e.date_layoff > j.jdate
)
)
group by j.jdate
order by j.jdate;
Note: Not tested.

Find orders in the last 12 months, max date being the last order date

I feel like this question has been asked before, I couldn't find the exact requirement or the database engine (PostgreSQL), so please feel free to link me to the original.
I have a seemingly simple requirement: given an orders table, I need to find orders in a 12-month range, but the ending date will be the max date found in the table. The column of interest is called order_date and stores dates in the YYYY-mm-dd format.
So, here's my attempt, but it fails, saying that the aliased column (max_order_date) doesn't exist:
SELECT * FROM orders WHERE order_date BETWEEN
(SELECT max(order_date) FROM orders as max_order_date)
AND (SELECT max_order_date - INTERVAL '1 year');
After seeing Postgres complain, I tried to write the query without an alias:
SELECT * FROM orders WHERE order_date BETWEEN
(SELECT max(order_date) FROM orders as max_order_date)
AND (SELECT max(order_date) - INTERVAL '1 year');
Only this time, I got this error: aggregate functions are not allowed in the WHERE clause.
I obviously am looking for the right way to do this query, but am also very eager to learn why the above two limitations exist. That is, why can't the aliasing work in the first case, and why aggregates aren't allowed in WHERE? To me it doesn't look like I'm asking for something twisted or impossible to compute.
Thank you!
This is probably more efficient using a subquery:
select o.*
from orders o
where o.order_date >= (select max(order_date) - interval '1 year' from orders);
Both the subquery and the outer query can use an index on orders(order_date).
You can use the analytical function of max() over() to determine the max order date and then use a filter to check if the order date lies between 1 year and max order date.
select *
from (SELECT o.*
,max(order_date) over(partition by 1) as max_order_date
FROM orders o
)x
where order_date>= x.max_order_date - INTERVAL '1 year'

How to count the number of orders in the first hour in SQL

Let's say have a table like:
DateTime - When the order was placed
CustomerId - The id of the customer
<other fields>
How do I create a query that will tell me the total number of orders a customer placed in the hour after creating their first order.
I'm currently finding the first order per customer, joining it back to the original table, and doing a countif but wondering if there is a way to do it in a single step.
In Standard SQL, you can use logic like this:
select customerid, count(*)
from (select t.*,
min(datetime) over (partition by customerid) as min_datetime
from t
) t
where datetime < min_datetime + interval '1 hour'
group by customerid;
Date/time functions differ significantly among databases, but this general structure should work in almost every database.

IF in sql to choose which values to select

I am trying to use an IF or CASE statement in sql to choose when to select a value in a column. Essentially I have some data in a table like so:
My goal is to see which items are ordered multiple weeks in a row by the same customer. I have 1 month of dates, but I can do 7 separate queries with 1 query for each day of the week. I'm trying to do something like:
Select item, date, customer, truck
If customer, item combo appears in multiple weeks
Please let me know if you have any idea how I can do this!
Assuming you have at most one row per week per customer and item (as in the sample data), you can use lead() and lag(). The following assumes that you mean exactly 7 days apart:
select t.*
from (select t.*,
lag(orderdate) over (partition by customer, itemid order by orderdate) as prev_orderdate,
lead(orderdate) over (partition by customer, itemid order by orderdate) as next_orderdate
from t
) t
where prev_orderdate = orderdate - interval '7 day' or
next_order_date = orderdate + interval '7 day';
Note that date/time functionality is highly database dependent, so you might have to adjust for your database functions.

PL-SQL query to calculate customers per period from start and stop dates

I have a PL-SQL table with a structure as shown in the example below:
I have customers (customer_number) with insurance cover start and stop dates (cover_start_date and cover_stop_date). I also have dates of accidents for those customers (accident_date). These customers may have more than one row in the table if they have had more than one accident. They may also have no accidents. And they may also have a blank entry for the cover stop date if their cover is ongoing. Sorry I did not design the data format, but I am stuck with it.
I am looking to calculate the number of accidents (num_accidents) and number of customers (num_customers) in a given time period (period_start), and from that the number of accidents-per-customer (which will be easy once I've got those two pieces of information).
Any ideas on how to design a PL-SQL function to do this in a simple way? Ideally with the time periods not being fixed to monthly (for example, weekly or fortnightly too)? Ideally I will end up with a table like this shown below:
Many thanks for any pointers...
You seem to need a list of dates. You can generate one in the query and then use correlated subqueries to calculate the columns you want:
select d.*,
(select count(distinct customer_id)
from t
where t.cover_start_date <= d.dte and
(t.cover_end_date > d.date + interval '1' month or t.cover_end_date is null)
) as num_customers,
(select count(*)
from t
where t.accident_date >= d.dte and
t.accident_date < d.date + interval '1' month
) as accidents,
(select count(distinct customer_id)
from t
where t.accident_date >= d.dte and
t.accident_date < d.date + interval '1' month
) as num_customers_with_accident
from (select date '2020-01-01' as dte from dual union all
select date '2020-02-01' as dte from dual union all
. . .
) d;
If you want to do arithmetic on the columns, you can use this as a subquery or CTE.