sql sum sales between date - sql

The image explains everything. My aim is to the last table using sql.
I will explain the logic with an example.
Table 1 shows the expected sales. For instance, article 22 should be sold only between 1 janv and 4 jan. To make it simple we assume that shops are open every day.
Table 2 shows the daily sales for each article. For instance article was sold 1 janv 2 janv 3 janv 4 janv. However, for article 22 4 janv is not in the range [1 janv to 3 janv]. Thus to get the sum of sales of article 22, we should omit the 4 jan. Therefore the calculation for article 22 is 2+4+5=11.
DataType
Table 1
artNo: int
from: date
planned_to: date
Table 2
artNo: int
day: date
sales: float

Something like this:
SELECT
t1.artNo
, t1.[from]
, t1.planned_to
, SUM(t2.sales) total
FROM
table1 t1
JOIN table2 t2 ON
t1.artNo = t2.artNo
AND t2.day >= t1.[from]
AND t2.day <= t1.planned_to
GROUP BY
t1.artNo
, t1.[from]
, t1.planned_to

SELECT t1.artNO
,t1.
,t1.planned_to
,SUM(sales) as Total
FROM table1 t1
INNER JOIN table2 t2 ON t1.artNo = t2.artNo
GROUP BY t1.artNO
,t1.
,t1.planned_to

Related

wants to pick most closest record in group of records in single table which input criteria

we have a table and there is possibility that one record can have multiple copies means same record can exist in table with multiple entries but their criteria will be different criteria is decided using three main parameters.income,score,no_months.these columns are integer.and we are grouping them by giving unique code to same records profile.
if one input is eligible for multiple profiles then we need to pick which is most matching to criteria.
Sample Data.
id
name
income
score
no_months
group_code
22
abc
1000
500
6
abccode
23
abc
900
600
12
abccode
24
bca
1000
600
12
bcacode
Desired Results
id
name
income
score
no_months
group_code
23
abc
900
600
12
abccode
24
bca
1000
600
12
bcacode
Note: id 23 row has 2 columns which values are greater than id 22 row that is why id 23 was picked although id 23 has less income
Only those records should be display which columns have more count of greater values than other row if group_code is same.
I have tried using multiple order by with cte as more columns needs to display like image city etc. but its not working
Select a single row for the Name or a winner of multiple rows. Winner is one with max score of wins when compared to others in a triangle join. Provided 2 rows has the same criteria, a row with the lesser id wins.
select *
from tbl t
where id in (
-- winners
select winid
from tbl t1
join tbl t2 on t1.name = t2.name and t1.id < t2.id
join lateral (
select case when sign(t1.income - t2.income) + sign(t1.score - t2.score) + sign(t1.no_months - t2.no_months) >= 0
then t1.id else t2.id end winid
) w on 1=1
group by winid
order by count(*) desc
limit 1)
or not exists(select 1 from tbl t3 where t3.name = t.name and t3.id <> t.id)

How to create a Select statement which contains a SUM on a different table

I currently have a select statement that is causing me issues, i have two tables:
Customer table
ID Month4Value Month5Value
1 24 5
Orders table
ID Year Month Value Quantity
1 2018 8 10 2
1 2018 4 2 1
1 2018 6 10 4
1 2018 4 7 3
I currently have the below view:
Create View Values as
Select ID, Year, Month, ROUND(SUM(Value*Quantity),2) as NewQuantity
FROM Orders
GROUP BY ID, Year, Month
The below select statement is what i am trying to run
Select Customer.ID, Customer.Month5Value, NewQuantity
from Customer inner join Values on Customer.ID = Values.ID
where ROUND(Customer.Month5Value, 2) <> ROUND(NewQuantity,2)
AND Values.Year = 2018
AND Values.Month = 5
What i am trying to achieve is to find any mismatches between the Orders table and the Customer table. In the above example, what i am expecting is to highlight that the value in Customer.Month5Value does not match the total of the (Quantity*Value) from the Orders table.
As there are 0 orders for Month 5 in the Orders Table, the Month5Value should be 0. However, it returns no entrys.
Any thoughts about what i have missed?
EDIT -
I have updated my query to this:
Select Customer.ID, Customer.Month5Value, NewQuantity
from Customer left join Values on Customer.ID = Values.ID
where ROUND(Customer.Month5Value, 2) <> ISNULL((Select NewQuantity from Customer left join Values on Customer.ID = Values.ID where Values.Month = 5 and Values.Year = 2018),0)
This has given me a list of IDs which have an incorrect amount in Month5Value on the Customer table, but displays lines for each month entry
ID Month5Value NewQuantity
1 5 24
1 5 40
1 5 20
How can i adjust this so that I get one line per ID with the correct value for NewQuantity (either 0 or NULL in this case)?
I think the INNER JOIN is removing any records which are missing from VALUES. Replacing the INNER JOIN with LEFT JOIN may give the result you are looking for.

divide column from one table to column from another table

I've two tables
TB_1 TB_3
Month Total Month Total
2012-01 6 2012-01 12
2013-02 6 2013-02 12
2014-03 10 2014-03 20
2015-04 10 2015-04 20
In result table I need follow result:
RESULT_TB
Month Total
2012-01 2
2013-02 2
2014-03 2
2015-04 2
I tried the following:
Select TB_3.total / TB_1.total
From TB_3, TB_1
But it does not work, tell me please, how to do?
Try this:
select t1.month,
case
when t1.total = 0
then null
else t2.total / t1.total
end total
from tb_1 t1
join tb_3 t2 on t1.month = t2.month
I used CASE to ensure if there is zero total in tb_1 table, the output comes as NULL instead of throwing a divide by zero error.
You only seem to need a proper JOIN condition:
Select t3.month, t3.total / t1.total
From TB_3 t3 JOIN
TB_1 t1
ON t3.month = t1.month;
Note: If the tables have different sets of months, then you might want some sort of outer join.
Also, Postgres does integer division when both operands are integers. It is unclear whether you want that or not. But if the values were "15" and "2", then the result would be "7" rather than "7.5".
You can try this code:
SELECT ISNULL(T1.Month,T2.Month) AS Month
, CASE WHEN ISNULL(T1.Total,0) = 0 THEN ISNULL(T2.Total,0)
ELSE ISNULL(T2.Total,0)/ISNULL(T1.Total,0)
END AS Total
FROM TB_1 AS T1
FULL JOIN TB_2 AS T2 ON T1.Month = T2.Month
I have used FULL JOIN to catch all dates from both tables. I have checked for all NULL values and replaced the NULLs by divisible values.
For matching months only, use this -
select TB_1.month as month
,TB_3.total / nullif(TB_1.total,0) as total
from TB_1
join TB_3
on TB_3.month = TB_1.month
order by month
If you wish to also display results for nonmatching months, use this -
select coalesce(TB_1.month,TB_3.month) as month
,TB_3.total / nullif(TB_1.total,0) as total
from TB_1
full join TB_3
on TB_3.month = TB_1.month
order by month
nullif(X,0) is a clean way to express-
Return X unless it is equal to 0, where in that case return a NULL
If the total column are of integer type, replace TB_3.total with TB_3.total::numeric(12,2) (or other precision as you like)

Aggregate column text where dates in table a are between dates in table b

Sample data
CREATE TEMP TABLE a AS
SELECT id, adate::date, name
FROM ( VALUES
(1,'1/1/1900','test'),
(1,'3/1/1900','testing'),
(1,'4/1/1900','testinganother'),
(1,'6/1/1900','superbtest'),
(2,'1/1/1900','thebesttest'),
(2,'3/1/1900','suchtest'),
(2,'4/1/1900','test2'),
(2,'6/1/1900','test3'),
(2,'7/1/1900','test4')
) AS t(id,adate,name);
CREATE TEMP TABLE b AS
SELECT id, bdate::date, score
FROM ( VALUES
(1,'12/31/1899', 7 ),
(1,'4/1/1900' , 45),
(2,'12/31/1899', 19),
(2,'5/1/1900' , 29),
(2,'8/1/1900' , 14)
) AS t(id,bdate,score);
What I want
What I need to do is aggregate column text from table a where the id matches table b and the date from table a is between the two closest dates from table b. Desired output:
id date score textagg
1 12/31/1899 7 test, testing
1 4/1/1900 45 testinganother, superbtest
2 12/31/1899 19 thebesttest, suchtest, test2
2 5/1/1900 29 test3, test4
2 8/1/1900 14
My thoughts are to do something like this:
create table date_join
select a.id, string_agg(a.text, ','), b.*
from tablea a
left join tableb b
on a.id = b.id
*having a.date between b.date and b.date*;
but I am really struggling with the last line, figuring out how to aggregate only where the date in table b is between the closest two dates in table b. Any guidance is much appreciated.
I can't promise it's the best way to do it, but this is a way to do it.
with b_values as (
select
id, date as from_date, score,
lead (date, 1, '3000-01-01')
over (partition by id order by date) - 1 as thru_date
from b
)
select
bv.id, bv.from_date, bv.score,
string_agg (a.text, ',')
from
b_values as bv
left join a on
a.id = bv.id and
a.date between bv.from_date and bv.thru_date
group by
bv.id, bv.from_date, bv.score
order by
bv.id, bv.from_date
I'm presupposing you will never have a date in your table greater than 12/31/2999, so if you're still running this query after that date, please accept my apologies.
Here is the output I got when I ran this:
id from_date score string_agg
1 0 7 test,testing
1 92 45 testinganother,superbtest
2 0 19 thebesttest,suchtest,test2
2 122 29 test3,test4
2 214 14
I might also note that between in a join is a performance killer. IF you have large data volumes, there might be better ideas on how to approach this, but that depends largely on what your actual data looks like.

Query number of rows that don't appear in date range of another table

Imagine I have a table of customers that have unsubscribed from something:
DateMonthID customerID
201301 123
201301 321
201302 987
201303 567
201303 728
etc etc
and another table of customers that are subscribers each month and their subscription
DateMonthID customerID subscriptionType
... ... 1
... ... 3
... ... 2
... ... 3
etc etc
I need to do a count of all the rows in the first table that don't appear in the second table for 3 months. For example, I need to count customer 987 if he does not appear in the second table between the months of 201302(feb) and 201305(may)
I currently have the following:
SELECT
COUNT(1) AS Total,
table1.DateMonthID AS MonthID
FROM
table1
WHERE
table1.DateMonthID <= 201212-3
AND NOT EXISTS (SELECT * FROM table2
WHERE (table2.DateMonthID >= table1.DateMonthID AND table2.DateMonthID <= (table1.month_key + 3))
AND table2.customerID = table1.customerID)
GROUP BY
table1.DateMonthID
This gives me outputs that look like
Total MonthID
1000 201301
2345 201302
4532 201303
986 201304
etc etc
This seems fine but what I want to do now is also group by the subscriptiontype. I'm sure this means I need to do a join but being quite new to SQL I'm clueless as to what join and where. I tried doing and inner join between customerIds but ended up having totals that exceeded the amount of record in table one for the corresponding months.
Try this query
SELECT COUNT(*) AS Total, table1.DateMonthID AS MonthID, subscribers.subscriptionType
FROM table1 JOIN subscribers ON table1.DateMonthID = subscribers.DateMonthID
AND table1.customerID = subscribers.customerID
WHERE table1.DateMonthID <= 201212 - 3
AND NOT EXISTS (SELECT *
FROM table2
WHERE (table2.DateMonthID >= table1.DateMonthID
AND table2.DateMonthID <= (table1.month_key + 3))
AND table2.customerID = table1.customerID)
GROUP BY table1.DateMonthID, subscribers.subscriptionType
This query, (or using count(*))
SELECT
COUNT(table1.DateMonthID) AS Total,
subscribers.subscriptionType
FROM
table1
INNER JOIN subscribers
ON table1.DateMonthID = subscribers.DateMonthID
AND table1.customerID = subscribers.customerID
WHERE
table1.DateMonthID <= 201212-3
AND NOT EXISTS (SELECT * FROM table2
WHERE (table2.DateMonthID >= table1.DateMonthID
AND table2.DateMonthID <= (table1.month_key + 3))
AND table2.customerID = table1.customerID)
GROUP BY subscribers.subscriptionType
will give you total number of records by subscriber.
If you want the break out by subscriber and month, add the DateMonthID in the Group.
GROUP BY subscribers.subscriptionType, table1.DateMonthID
Remember to add table1.DateMonthID to the select to see it on the result.