How to merge dates between 2 locations using SQL? - sql

TIA for any assistance towards this problem, I am rather new to SQL/SSMS.
I would like to understand how I can create date rows for 1 location based on another. For example, I have the following table:
ClientFK | LocationFK | Month | Sales
---------|------------|-----------|-------
15 |1 |2019-04-01 | $100
15 |2 |2019-04-01 | $50
15 |2 |2019-03-01 | $30
15 |2 |2019-02-01 | $20
How can I create rows in location 1 in which location 2 had sales? The output would look like this:
ClientFK | LocationFK | Month | Sales
---------|------------|-----------|-------
15 |1 |2019-04-01 | $100
15 |1 |2019-03-01 | $0
15 |1 |2019-02-01 | $0
15 |2 |2019-04-01 | $50
15 |2 |2019-03-01 | $30
15 |2 |2019-02-01 | $20
My goal is to make this dynamic, so I'm not trying to work with this specific client/location, it is just an example. Ideally this should work for any client/location combo.
Again, I'm rather new to this, and wasn't sure how to best ask this question. Any advice on how to clarify what I'm asking would be much appreciated as well. Thanks!

This will give you all months for all locations and all clients.
with AvailableDates as(
select distinct [Month] from MonthlySales
),
Locations as(
select distinct LocationFk from MonthlySales
),
Clients as(
select distinct ClientFk from MonthlySales
)
select c.ClientFk, l.LocationFk, ad.Month, IsNull(ms.Sales,0) as Sales
from Locations l
left join AvailableDates ad on 1=1
left join clients c on 1=1
left join MonthlySales ms on
l.LocationFk=ms.LocationFk
and c.ClientFk = ms.ClientFk
and ad.Month = ms.Month
order by locationFK, Month desc

Here is a way to go about with this. First find out all of the unique months for Location2.
Then carterisan prod with the Client1 data.
After that you have all of the records for Client1 and Client2 in the carteisan prod block, which would left join with the data and set up the sales=0 for those missing months data.
with loc2_data
as (select distinct month
from tbl
where locationfk=2
and ClientFK=15
)
,cartesian_prod
(
select a.ClientFK,a.LocationFK,b.Month
from tbl a
join loc2_data b
on 1=1
where a.locationfk=1
and a.ClientFK=15
)
select a.ClientFK,a.LocationFK,a.Month,isnull(v.Sales,0) as sales
from cartesian_prod a
left join tbl v
on a.ClientFK=v.ClientFK
and a.LocationFK=v.LocationFK
and a.Month=v.Month

Related

PostgreSQL - return months even where there are no records within that month

first of all thank you for helping me, this is my first JR job and I don't want to screw this up.
I need to return all the records (grouped by price and money_balance) of a Debts history by month, of Property for a specific year.
What I've been trying to do is
SELECT properties.name as property
, EXTRACT(month from priority_date) as month
, SUM(debts.money_balance) as money_balance
, SUM(debts.price) as price
FROM properties
JOIN debts on properties.id = debts.property_id
WHERE properties.community_id = 15
AND properties.active = TRUE
AND EXTRACT(year from priority_date) = 2021
GROUP BY month, properties.name
This is going to give me something like
id
property
month
money_balance
price
1
A1
1
1111
3131
2
A1
7
0
1111
3
A2
7
0
1111
But I need to have even months where there are no records, and to have the money_balance and price at 0 or null, if this achievable with SQL?
Thank you so much.
Edit:
Desired output:
|id|property | month|money_balance| price|
|--| --- | ---- |-- | ---- |
|1 | A1 |1 |1111 | 3131 |
|2 |A1 |2 | 0 |0 |
|3 |A1 |3 | 0 |0 |
Till month 12, it ca be 0 or null in the months were the are no records
You can use generate_series() to generate all the months, and then bring the data in:
SELECT p.name as property, mon,
SUM(d.money_balance) as money_balance,
SUM(d.price) as price
FROM GENERATE_SERIES(1, 12, 1) gs(mon) JOIN
(properties p JOIN
debts d
ON p.id = d.property_id AND
p.community_id = 15 AND
p.properties.active = TRUE AND
EXTRACT(year from priority_date) = 2021
)
ON EXTRACT(month from priority_date) = gs.mon
GROUP BY gs.mon, p.name

Creating user time report that includes zero hour weeks

I'm having a heck of a time putting together a query that I thought would be quite simple. I have a table that records total hours spent on a task and the user that reported those hours. I need to put together a query that returns how many hours a given user charged to each week of the year (including weeks where no hours were charged).
Expected Output:
|USER_ID | START_DATE | END_DATE | HOURS |
-------------------------------------------
|'JIM' | 4/28/2019 | 5/4/2019 | 6 |
|'JIM' | 5/5/2019 | 5/11/2019 | 0 |
|'JIM' | 5/12/2019 | 5/18/2019 | 16 |
I have a function that returns the start and end date of the week for each day, so I used that and joined it to the task table by date and summed up the hours. This gets me very close, but since I'm joining on date I obviously end up with NULL for the USER_ID on all zero hour rows.
Current Output:
|USER_ID | START_DATE | END_DATE | HOURS |
-------------------------------------------
|'JIM' | 4/28/2019 | 5/4/2019 | 6 |
| NULL | 5/5/2019 | 5/11/2019 | 0 |
|'JIM' | 5/12/2019 | 5/18/2019 | 16 |
I've tried a few other approaches, but each time I end up hitting the same problem. Any ideas?
Schema:
---------------------------------
| TASK_LOG |
---------------------------------
|USER_ID | DATE_ENTERED | HOURS |
-------------------------------
|'JIM' | 4/28/2019 | 6 |
|'JIM' | 5/12/2019 | 6 |
|'JIM' | 5/13/2019 | 10 |
------------------------------------
| DATE_HELPER_TABLE |
|(This is actually a function, but I|
| put it in a table to simplify) |
-------------------------------------
|DATE | START_OF_WEEK | END_OF_WEEK |
-------------------------------------
|5/3/2019 | 4/28/2019 | 5/4/2019 |
|5/4/2019 | 4/28/2019 | 5/4/2019 |
|5/5/2019 | 5/5/2019 | 5/11/2019 |
| ETC ... |
Query:
SELECT HRS.USER_ID
,DHT.START_OF_WEEK
,DHT.END_OF_WEEK
,SUM(HOURS)
FROM DATE_HELPER_TABLE DHT
LEFT JOIN (
SELECT TL.USER_ID
,TL.HOURS
,DHT2.START_OF_WEEK
,DHT2.END_OF_WEEK
FROM TASK_LOG TL
JOIN DATE_HELPER_TABLE DHT2 ON DHT2.DATE_VALUE = TL.DATE_ENTERED
WHERE TL.USER_ID = 'JIM1'
) HRS ON HRS.START_OF_WEEK = DHT.START_OF_WEEK
GROUP BY USER_ID
,DHT.START_OF_WEEK
,DHT.END_OF_WEEK
ORDER BY DHT.START_OF_WEEK
http://sqlfiddle.com/#!18/02d43/3 (note: for this sql fiddle, I converted my date helper function into a table to simplify)
Cross join the users (in question) and include them in the join condition. Use coalesce() to get 0 instead of NULL for the hours of weeks where no work was done.
SELECT u.user_id,
dht.start_of_week,
dht.end_of_week,
coalesce(sum(hrs.hours), 0)
FROM date_helper_table dht
CROSS JOIN (VALUES ('JIM1')) u (user_id)
LEFT JOIN (SELECT tl.user_id,
dht2.start_of_week,
tl.hours
FROM task_log tl
INNER JOIN date_helper_table dht2
ON dht2.date_value = tl.date_entered) hrs
ON hrs.user_id = u.user_id
AND hrs.start_of_week = dht.start_of_week
GROUP BY u.user_id,
dht.start_of_week,
dht.end_of_week
ORDER BY dht.start_of_week;
I used a VALUES clause here to list the users. If you only want to get the times for particular users you can do so too (or use any other subquery, or ...). Otherwise you can use your user table (which you didn't post, so I had to use that substitute).
However the figures that are produced by this (and your original query) look strange to me. In the fiddle your user has worked for a total of 23 hours in the task_log table. Yet your sums in the result are 24 and 80, that is way to much on its own and even worse taking into account, that 1 hour in task_log isn't even on a date listed in date_helper_table.
I suspect you get more accurate figures if you just join task_log, not that weird derived table.
SELECT u.user_id,
dht.start_of_week,
dht.end_of_week,
coalesce(sum(tl.hours), 0)
FROM date_helper_table dht
CROSS JOIN (VALUES ('JIM1')) u (user_id)
LEFT JOIN task_log tl
ON tl.user_id = u.user_id
AND tl.date_entered = dht.date_value
GROUP BY u.user_id,
dht.start_of_week,
dht.end_of_week
ORDER BY dht.start_of_week;
But maybe that's just me.
SQL Fiddle
http://sqlfiddle.com/#!18/02d43/65
Using your SQL fiddle, I simply updated the select statement to account for and convert null values. As far as I can tell, there is nothing in your post that makes this option not viable. Please let me know if this is not the case and I will update. (This is not intended to detract from sticky bit's answer, but to offer an alternative)
SELECT ISNULL(HRS.USER_ID, '') as [USER_ID]
,DHT.START_OF_WEEK
,DHT.END_OF_WEEK
,SUM(ISNULL(HOURS,0)) as [SUM]
FROM DATE_HELPER_TABLE DHT
LEFT JOIN (
SELECT TL.USER_ID
,TL.HOURS
,DHT2.START_OF_WEEK
,DHT2.END_OF_WEEK
FROM TASK_LOG TL
JOIN DATE_HELPER_TABLE DHT2 ON DHT2.DATE_VALUE = TL.DATE_ENTERED
WHERE TL.USER_ID = 'JIM1'
) HRS ON HRS.START_OF_WEEK = DHT.START_OF_WEEK
GROUP BY USER_ID
,DHT.START_OF_WEEK
,DHT.END_OF_WEEK
ORDER BY DHT.START_OF_WEEK
Create a dates table that includes all dates for the next 100 years in the first column, the week of the year, day of the month etc in the next.
Then select from that dates table and left join everything else. Do isnull function to replace nulls with zeros.

Count and max aggregate function in same table in one query

I have to do count and max aggregate function in same query. For example I have history table contains date column. I need to retrieve the latest date as well as count () with some criteria. Criteria is applicable for only count() . I am able to retrieve the latest date using max and rank function.But could not merge both. Could you please assist?
Update:
Scenario : Customer buys/sells Shares.
Input: Table Share_history and Table Customer and Table Share and Table Share_Status
Customer :
Cust_id |Cust_name
1 |A
2 |B
Share :
Share_id|Share_Name|Owner|
10 |ABC |XYZ |
20 |BCD |MNC |
Share_Status :
Share_Status_Id|Share_Status_Name
1 |Buy
2 |Sell
Share_history :
Share_history _id|Share_id|Trans_date|Share_status_Id|Cust_id
100 |10 |12/12/14 | 1 |1
101 |10 |24/12/14 | 2 |1
102 |10 |14/01/15 | 1 |1
103 |10 |28/02/15 | 2 |1
103 |10 |16/03/15 | 1 |1
Output: latest Trans_date and count(no of times specific share was bought(1)) and Cust_id=1.
Query:
select share1.Share_id,SHAREHIST.Latest_Date,SHAREHIST.buycount
from Share share1 left outer join
(select share_id,max(Trans_date) keep(dense_rank last order by share_id) as Latest_Date,
(select count(*) as buycount from Share_history where Share_status_id=1 and Share_id=share1.Share_id)
from Share_history
group by Share_id
) SHAREHIST
on SHAREHIST.share_id=share1.share_id
EXPECTED :
Share_id|Latest_Date|buycount
10 |16/03/15 | 3
Try using this:
SELECT
Share_id
,Trans_Date
,COUNT(Share_id) buycount
FROM
(
SELECT
*
FROM Share_history SH
WHERE Trans_Date = (SELECT MAX(Trans_Date) FROM Share_history)
) SH
GROUP BY Share_id, Trans_Date
Rest of the joins I think you can add.
I think you just want aggregation:
select sh.share_id, max(trans_date) as trans_date, count(*) as buy_count,
from share_history sh
where cust_id = 1
group by sh.share_id;

SQL (sqlite) compare sums of rows grouped by another repeating row

I have a table like:
|------------------------|
|day name trees_planted|
|------------------------|
|1 | alice | 3 |
|2 | alice | 4 |
|1 | bob | 2 |
|2 | bob | 4 |
|------------------------|
I'm using SELECT name, SUM(trees_planted) FROM year2016 GROUP BY name to get:
name | trees_planted
alice | 7
bob | 6
But then I have another table from 2015 and I want to compare the results with the previous year, if for example Alice planted more trees in 2016 than in 2015 I'd get a result like this:
name | tree_difference
alice | -2 (if previous year she planted 5 trees, 5 -7 = -2)
bob | 0 (planted the same number of trees last year)
You could use a sub-query to get the records from both 2016 and 2015, but negate the values from 2016. Then group and sum like you already did:
SELECT name,
SUM(trees_planted) AS tree_difference
FROM (SELECT name, trees_planted
FROM year2015
UNION ALL
SELECT name, -trees_planted
FROM year2016
) AS years
GROUP BY name
This will also work for cases where a number is only given in one of the two years.
Assuming you can join using user field, you can do:
select a.name, a.tp, b.tp, a.tp - b.tp
from
(
(select name, SUM(trees_planted) tp from year2016 group by name) a
inner join
(select name, SUM(trees_planted) tp from year2015 group by name) b
using(name)
)
If you can't join on field user (you have different set of users in 2015 and 2016), it'll be easy to add the missing information by using a couple of union clauses.
Here's a link with artificial data to SQLFIDDLE to try the query.

Merging rows SQL - Access

I have this table on MS Access:
Name | Week | Manager | Sales
John | 201409 | Marcelo | 53
John | 201410 | Marcelo | 20
John | 201410 | Raquel | 30
John | 201411 | Raquel | 53
I have to merge Week 201410 by the max Sales and choose which Manager. After this I'd like to sum the Total Sales for this two and make like this:
Name | Week | Manager | Sales
John | 201409 | Marcelo | 53
John | 201410 | Raquel | 50
John | 201411 | Raquel | 53
Could anybody help me? I tried a lot of SQL and couldn't do nothing useful.
You can try this:
SELECT [Name], [Week], [Manager], SUM([Sales]) as Sales1
From [YourTable]
GROUP BY [Name], [Week], [Manager]
I did not test this so let me know what errors you get.
If each row had a unique identifier (Primary Key), it would be a lot simpler. However, you work with the data you have, not with the data you wish you had, so here's my circuitous way of accomplishing it. You could combine this all into one query and avoid using temporary tables; I split it out this way to make it convenient to understand, rather than being concise.
First, extract the highest Sales for each Name-Week combination:
SELECT Name, Week, MAX(Sales)
INTO #MaxSales
FROM [YourTable]
GROUP BY Name, Week
Use this information to get the Manager that you should use for each week (We use TOP 1 to resolve the case where two managers have the same sales for the same Name/Week; I'm not sure how you would want to resolve this.):
SELECT Name, Week, Manager
INTO #MaxSalesManager
FROM [YourTable]
INNER JOIN #MaxSales
ON [YourTable].Name = #MaxSales.Name
AND [YourTable].Week = #MaxSales.Week
WHERE [YourTable].Sales = #MaxSales.Sales
Now you can extract the information you need:
SELECT [YourTable].Name, [YourTable].Week, #MaxSalesManager.Manager, SUM([YourTable].Sales)
FROM [YourTable]
INNER JOIN #MaxSalesManager
ON [YourTable].Name = #MaxSalesManager.Name
AND [YourTable].Week = #MaxSalesManager.Week
GROUP BY [YourTable].Name, [YourTable].Week, #MaxSalesManager.Manager
Hope this helps!
EDIT:
Combining them all into one query:
SELECT [YourTable].Name,
[YourTable].Week,
#MaxSalesManager.Manager,
SUM([YourTable].Sales)
FROM [YourTable]
INNER JOIN
(SELECT Name, Week, Manager
FROM [YourTable]
INNER JOIN
(SELECT Name, Week, MAX(Sales)
FROM [YourTable]
GROUP BY Name, Week) AS #MaxSales
ON [YourTable].Name = #MaxSales.Name
AND [YourTable].Week = #MaxSales.Week
WHERE [YourTable].Sales = #MaxSales.Sales) AS #MaxSalesManager
ON [YourTable].Name = #MaxSalesManager.Name
AND [YourTable].Week = #MaxSalesManager.Week
GROUP BY [YourTable].Name, [YourTable].Week, #MaxSalesManager.Manager