Annual sales change - postgresql

my query so far using joins: quantity - number of goods purchased, date - date of transaction, name - customer_name, year - year of transaction. Similar to the daily change, I tried using an annual difference and pass in the query to satisfy the conditions.
SELECT s1.year, s1.name, sum(s1.quantity)
FROM T1 s1
JOIN T2 s2
ON s1.date::timestamp - s2.date::timestamp = '1 year'
WHERE (s1.quantity > s2.quantity AND s1.year BETWEEN 2015 and 2017)
OR (s1.quantity < s2.quantity AND s1.year BETWEEN 2018 and 2019)
GROUP BY 1,2

with data as (
SELECT name,
COALESCE(SUM(CASE WHEN year = 2015 THEN qty ELSE 0 END), 0) AS total2015,
COALESCE(SUM(CASE WHEN year = 2016 THEN qty ELSE 0 END), 0) AS total2016,
COALESCE(SUM(CASE WHEN year = 2017 THEN qty ELSE 0 END), 0) AS total2017,
COALESCE(SUM(CASE WHEN year = 2018 THEN qty ELSE 0 END), 0) AS total2018,
COALESCE(SUM(CASE WHEN year = 2019 THEN qty ELSE 0 END), 0) AS total2019
FROM sajith_gowthaman.practice_annual_inc1
WHERE year BETWEEN 2015 and 2019
GROUP BY name
)
select *
from data
where total2015 < total2016
and total2016 < total2017
and total2018 > total2019;

Related

12 months rolling

I have the query below which returns 12 months rolling data. So if I run it today it brings data back from 23rd August 2015 to 23rd August 2016. Now ideally I would like it to start from the 1st August 2015 and if I was to run it again next month it would start from 1stSeptember 2015. Is this possible to do? Thanks
select
Date
Street
Town
Incidents
IncidentType A
IncidentType B
IncidentType C
FROM
(
select
COUNT(I.INC_NUM) as Incidents,
COUNT(case when i.INC_TYPE = ''A'' THEN 1
end)
"IncidentType A"
COUNT(case when i.INC_TYPE = ''B'' THEN 1
end)
"IncidentType B"
COUNT(case when i.INC_TYPE = ''C'' THEN 1
end)
"IncidentType C"
FROM Table i
GROUP BY i.INC_NUM
) i
where Date >= (now()-('12 months'::interval))
Your code suggests that you are using Postgres. If the code works and you just need to adjust the where clause, use date_trunc():
where Date >= date_trunc('month', now() - ('12 months'::interval))

Number of entries between dates

I have a table with the following structure: -
day, id
2016-03-13, 123
2016-03-13, 123
2016-03-13, 231
2016-03-14, 231
2016-03-14, 231
2016-03-15, 129
And I'd like to build a table that looks like: -
id, d1, d7, d14
123, 1, 1, 1
231, 1, 2, 2
129, 1, 1, 1
Essentially for a given id, list the number of days which have an entry within a time window. So if id 123 has 10 entries within the last 14 days - d14 would be 10.
So far I have: -
SELECT
day,
id
FROM
events
WHERE
datediff (DAY, day, getdate()) <= 7
GROUP BY
day,
id
This query will do:
SELECT
id,
COUNT(DISTINCT CASE WHEN current_date - day <= 1 THEN 1 END) d1,
COUNT(DISTINCT CASE WHEN current_date - day <= 7 THEN 1 END) d7,
COUNT(DISTINCT CASE WHEN current_date - day <= 14 THEN 1 END) d14
FROM
events
GROUP BY
id
ORDER BY
id
Or, since PostgreSQL 9.4, slightly more concise:
SELECT
id,
COUNT(DISTINCT day) FILTER (WHERE current_date - day <= 1) d1,
COUNT(DISTINCT day) FILTER (WHERE current_date - day <= 7) d7,
COUNT(DISTINCT day) FILTER (WHERE current_date - day <= 14) d14
FROM
events
GROUP BY
id
ORDER BY
id
try this:
SELECT id
, count(case when DAY = getdate() then 1 else null end) as d1
, count(case when DAY + 7 >= getdate() then 1 else null end) as d7
, count(case when DAY + 14 >= getdate() then 1 else null end) as d14
FROM events
WHERE DAY between DAY >= getdate() - 14
--or if you can have day > today ... and DAY between getdate() - 14 and getdate()
GROUP By id

postgresql daysdiff between two dates grouped by month

I have a table with the date columns (start_date, end_date) and I want to calculate the difference between these dates and grouped by the month.
I am able to get the datediff in days, but I do not know how to group this in month, any suggestions?
Table:
id Start_date End_date days
1234 2014-06-03 2014-07-05 32
12345 2014-02-02 2014-05-10 97
Expected results:
month diff_days
2 26
3 30
4 31
5 10
6 27
7 5
I think your expected output numbers are off a little. You might want to double-check.
I use a calendar table myself, but this query uses a CTE and date arithmetic. Avoiding the hard-coded date '2014-01-01' and the interval for 365 days is straightforward, but it makes the query harder to read, so I just used those values directly.
with your_data as (
select date '2014-06-03' as start_date, date '2014-07-05' as end_date union all
select '2014-02-02', '2014-05-10'
), calendar as (
select date '2014-01-01' + (n || ' days')::interval calendar_date
from generate_series(0, 365) n
)
select extract (month from calendar_date) calendar_month, count(*) from calendar
inner join your_data on calendar.calendar_date between start_date and end_date
group by calendar_month
order by calendar_month;
calendar_month count
--
2 27
3 31
4 30
5 10
6 28
7 5
As a rule of thumb, you should never group by the month alone--doing that risks grouping data from different years. This is a safer version that includes the year, and which also restricts output to a single calendar year.
with your_data as (
select date '2014-06-03' as start_date, date '2014-07-05' as end_date union all
select '2014-02-02', '2014-05-10'
), calendar as (
select date '2014-01-01' + (n || ' days')::interval calendar_date
from generate_series(0, 700) n
)
select extract (year from calendar_date) calendar_year, extract (month from calendar_date) calendar_month, count(*) from calendar
inner join your_data on calendar.calendar_date between start_date and end_date
where calendar_date between '2014-01-01' and '2014-12-31'
group by calendar_year, calendar_month
order by calendar_year, calendar_month;
SQL Fiddle
with min_max as (
select min(start_date) as start_date, max(end_date) as end_date
from t
), g as (
select daterange(d::date, (d + interval '1 month')::date, '[)') as r
from generate_series(
(select date_trunc('month', start_date) from min_max),
(select end_date from min_max),
'1 month'
) g(d)
)
select *
from (
select
to_char(lower(r), 'YYYY Mon') as "Month",
sum(upper(r) - lower(r)) as days
from (
select t.r * g.r as r
from
(
select daterange(start_date, end_date, '[]') as r
from t
) t
inner join
g on t.r && g.r
) s
group by 1
) s
order by to_timestamp("Month", 'YYYY Mon')
;
Month | days
----------+------
2014 Feb | 27
2014 Mar | 31
2014 Apr | 30
2014 May | 10
2014 Jun | 28
2014 Jul | 5
Range data types
Range functions and operators

Calculated balance of purchased lots

I have a list of purchases by date. EG:
ItemCode, Purchase Date, Purchase Qty
XXX, 01 Jan 2012, 10
XXX, 10 Jan 2012, 5
For the item I have a corresponding Sales transactions:
Item, Sales Date, Sales Qty
XXX, 02 Jan 2012, -5
XXX, 09 Jan 2012, -3
XXX, 11 JAN 2012, -3
I am looking to get a SQL query (Without a cursor), to get the balance on each purchase order quantity. I.e Run each purchase (First in first out) to 0. (For the purposes of aging inventory )
How can you join the Purchases to the Sales to get this balance remaining each purchased Inventory Lot? Is this possible without a cursor?
Yes.
You union the two tables together, and run a running total on the resulting set.
;with cte as
(
select itemcode, purchasedate as tdate, purchaseqty as qty from purchases
union
select itemcode, salesdate, salesqty from sales
)
select
t1.*,
SUM(t2.qty)
from cte t1
left join cte t2
on t1.tdate>=t2.tdate
and t1.item = t2.item
group by t1.item, t1.pdate, t1.qty
To get the stock remaining at any particular time the same principal applies.
select p1.*,
case when (select SUM(abs(qty)) from sales) > SUM(p2.qty) then 0
else SUM(p2.qty) - (select SUM(abs(qty)) from sales) end as stockremaining
from purchases p1
left join purchases p2 on p1.item = p2.item
and p2.purchasedate <= p1.purchasedate
group by p1.purchasedate, p1.item, p1.qty
gives
1 2012-01-01 10 0
1 2012-01-10 5 4

Count orders by days of the week, adding Saturday & Sunday counts to Friday

I would like to get the count of ordered items from monday to sunday but adding saturday and sunday orders to fridays, so the query results would only display Orderdates (Monday to Friday)
I have this sql already that shows orders for every single day of the week:
select DATENAME(weekday,orderdate) Day,CONVERT(VARCHAR(10), orderdate, 103) orderdate,
COUNT(orderdate) Orders
from Orders_tb
where orderDate >= '2012-03-01 00:00:00.000'
and orderDate <= '2012-03-31 00:00:00.000'
group by datepart(day,orderDate),orderdate,DATENAME(weekday,orderdate)
Thanks for your input!
EDIT after clarification.
Use case to change weekend days to friday. Derived table is employed to avoid the need to replicate the same expression everywhere orderdate is needed.
select DATENAME(weekday,orderdate_trimmed) Day,
CONVERT(VARCHAR(10), orderdate_trimmed, 103) orderdate,
COUNT(orderdate_trimmed) Orders
from
(
select *,
order_date -
case DATENAME(weekday,orderdate)
when 'Saturday' then 1
when 'Sunday' then 2
else 0
end
orderdate_trimmed
from Orders_tb
) a
where orderDate >= '2012-03-01 00:00:00.000'
and orderDate <= '2012-03-31 00:00:00.000'
group by orderdate_trimmed
You might count matching days only by use of case statement:
select COUNT(orderdate) TotalOrders,
COUNT(CASE WHEN DATENAME(weekday,orderdate) = 'Monday' then 1 end) Monday,
COUNT(CASE WHEN DATENAME(weekday,orderdate) = 'Tuesday' then 1 end) Tuesday,
COUNT(CASE WHEN DATENAME(weekday,orderdate) = 'Wednesday' then 1 end) Wednesday,
COUNT(CASE WHEN DATENAME(weekday,orderdate) = 'Thursday' then 1 end) Thursday,
COUNT(CASE WHEN DATENAME(weekday,orderdate) = 'Friday'
OR DATENAME(weekday,orderdate) = 'Saturday'
OR DATENAME(weekday,orderdate) = 'Sunday'
THEN 1 end) Friday
from Orders_tb
where orderDate >= '2012-03-01 00:00:00.000'
and orderDate <= '2012-03-31 00:00:00.000'
A warning about dates: as a date can contain time portion it would be wiser to compare like this:
where orderDate >= '2012-03-01 00:00:00.000'
and orderDate < '2012-04-01 00:00:00.000'