Referencing different row in different column with same date - sql

Please find sample date below. I want to create a new column Payment_received, that finds payment_dates which are not NULL, and subtracts (payment date - earliest SMS date for that account number). For example, for account number 12345, the calculation would be (2021-07-22 - 2021-07-20) = 2 days and for account number 99999, the calculation would be (2021-08-13 - 2021-08-10) = 3 days. I was thinking if I could create a case when to do this calculation, however, I don't know how to reference different rows for the same account number.
SMS_Date Account Number Payment_Date Payment_received
2021-07-20 12345 NULL NULL
2021-07-21 12345 NULL NULL
2021-07-22 12345 2021-07-22 2
2021-08-10 99999 NULL NULL
2021-08-11 99999 NULL NULL
2021-08-12 99999 NULL NULL
2021-08-13 99999 2021-08-13 3

Use CROSS APPLY to find earliest SMS date for each row in original table.
SELECT
pi.*,
CASE
WHEN pi.Payment_Date IS NOT NULL THEN DATEDIFF(d, smsdate.First_SMS_Date, pi.Payment_Date)
ELSE NULL
END AS Payment_Received
FROM
PaymentsInfo pi
CROSS APPLY (
SELECT
MIN(SMS_Date) AS First_SMS_Date
FROM
PaymentsInfo smst
WHERE
pi.Payment_Date IS NOT NULL AND pi.Account_Number = smst.Account_Number
) smsdate

Related

Transforming a dataset containing bank transactions into SQL Server

I would like to transform a dataset containing some bank transactions.
The ultimate goal is to make a report in Power BI to track daily expenses.
For this, I have the following situation that gives me a headache. :)
This is an example:
Date
Transaction_Details
Debit
Credit
21 Jan 2023
Transfer HomeBank
500
NULL
NULL
Reference: 4944
NULL
NULL
NULL
Beneficiary: David John
NULL
NULL
NULL
In Bank Account: RO97INGB1111333380218
500
NULL
20 Jan 2023
POS Payment
36
NULL
NULL
Card number: xxxx xxxx xxxx 1020
NULL
NULL
NULL
Terminal: OZKARDES A/S
NULL
NULL
NULL
Date: 19-01-2023
NULL
NULL
The desired output would be to transpose all rows in Transaction_Details that have NULL values in Date column, into a new column (e.g Other_Details) and for each transaction to add another column with "Transaction_Key".
Below, I have attached an example:
Transaction_Key
Date
Transaction_Details
Other_Details
Debit
Credit
1
21 Jan 2023
Transfer HomeBank
Reference: 4944, Beneficiary: David John, In Bank Account: RO97INGB1111333380218
500
NULL
2
20 Jan 2023
POS Payment
Card number: xxxx xxxx xxxx 1020, Terminal: OZKARDES A/S, Date: 19-01-2023
36
NULL
I used some COALESCE functions but it didn't work.
If we can assume you are able to create an Id/Sequence either in the data source or when importing the data, such that you end up with an incrementing number per row, then by using a windowed aggregation as follows you can convert your data as required:
select Transaction_Key,
Max(Date) Date,
Max(case when date is not null then Transaction_Details end) Transaction_Details,
String_Agg(case when date is null then Transaction_Details end, ',') Other_details,
Max(case when date is not null then Debit end) Debit,
Max(case when date is not null then Credit end) Credit
from (
select *,
Sum(case when date is null then 0 else 1 end) over(order by id) as Transaction_Key
from t
)t
group by Transaction_Key;
See this example Fiddle

How to get the last day of the month without LAST_DAY() or EOMONTH()?

I have a table t with:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-29
1
123
10
2021-10-30
1
123
9
2021-10-31
1
123
8
2021-10-29
1
456
100
2021-10-30
1
456
90
2021-10-31
1
456
80
2021-10-29
2
123
18
2021-10-30
2
123
17
2021-11-29
2
456
18
I need to find the AMOUNT of each PRODUCT_ID for each combination of LOCATION + PRODUCT_ID.
If a PRODUCT_ID has no entry for that day the AMOUNT is NULL.
So the result should look like:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-31
1
123
8
2021-10-31
1
456
80
2021-10-31
2
123
NULL
2021-11-30
2
456
NULL
Sadly EXASOL has no LAST_DAY() or EOMONTH() function. How can I solve this?
You can get to the last day of the month using a date_trunc function in combination with date_add:
case
when t.date = date_add('day', -1, date_add('month', 1, date_trunc('month', t.date)))
then 'Y' else 'N' end as end_of_month
That being said, if you group your table for all combinations of locations and products, you will not get NULLs for products without sales on the last day of the month as shown in your output table.
When you group your data, any value that does not exist will simply not show up in your output table. If you want to force nulls to show up, you can create a new table that contains all combinations of products, locations, and hard-coded end of month dates.
Then, you can left join your old table with this new hard-coded table by date, location, and product. This method will give you the NULL values you expect.

CASE statement in the WHERE clause, with further conditioning after THEN

I have a table of invoices and one of contracts that invoices are linked to. However, not every invoice is linked to a contract. I want to return all invoices linked to contracts which are within a timeframe (Starting before 2021-03-01), and also the invoices with no contracts linked at all. I believe this is something like: contract IS null OR CASE WHEN contract IS NOT NULL THEN (condition on timestamp), but I don't know how to write it. Note that the actual conditioning will be done on multiple columns, so I am looking for the general form of multiple sub-conditions under the conditions in the WHERE clause.
Example:
Invoice Table
InvoiceID
ContractID
1
NULL
2
1
3
NULL
4
2
5
3
6
4
Contract Table
ContractID
Contract Start Timestamp
1
2021-01-01 00:00:00
2
2021-02-01 00:00:00
3
2021-03-02 00:00:00
4
2021-05-01 00:00:00
Desired Result
InvoiceID
ContractID
1
NULL
2
1
3
NULL
4
2
maybe a simple left join like this can help you in filtering
select
I.*
from Invoice I
left outer join Contract C -- left join gets even the NULL contractid invoices
on C.ContractID= I.ContractID
where
C.[Contract Start Timestamp] IS NULL
OR C.[Contract Start Timestamp]< '2021-03-01'

Select users based on their date joined and date left

I have the following table:
Person
UserID Name Date_Joined Date_Left
1 Test 2018-08-10 NULL
2 Test2 2018-07-10 NULL
3 Test3 2018-07-10 2018-12-31
4 Test4 2018-08-10 2018-09-10
I want to check by only their join and/or left date if they are billable(=active) or not.
These are billable users:
User whose start month is at least one month before billdate
with no date left
User whose start month is at least one month before billdate
with date left month equal to the billdate or later than the
billdate
Month of billing = always the previous month.
I use the following query:
DECLARE #billdate AS DATE = '2018-09-01';
SELECT *
FROM person
WHERE CompanyID = 1205 AND (
(
date_joined <= EOMONTH(#billdate, -1)
)
OR
(
date_left > EOMONTH(#billdate, -1) AND
date_left <= EOMONTH(#billdate)
)
)
My problems:
User Test4 is still present in my table if I set the billdate to 2018-11-01.
User Test3 dissapears if i set billdate to 2019-01-01
What is wrong with my query and how can I optimize this?
Sample data:
User list:
1 - Test - 2018-08-10 - NULL
2 - Test2 - 2018-07-10 - NULL
3 - Test3 - 2018-07-10 - 2018-12-31
4 - Test4 - 2018-08-10 - 2018-09-10
For the bill period of the previous month (= 8 / August) = #billdate 2018-09-10, these are the billable users:
Test2
Test3
However, when I change the bill period to 10 / october, these are the billable users:
Test
Test2
Test3
If I understand your logic correctly, a billable user is one whose start month is at least one month before the current month, and whose end month is less than or equal to the current month.
SELECT *,
CASE WHEN #billdate >= DATEADD(d, 1, EOMONTH(date_joined)) AND
(#billdate <= DATEADD(d, 1, EOMONTH(date_left)) OR date_left IS NULL)
THEN 'billable'
ELSE 'not billable' END AS status
FROM person;
This billing logic appears to be consistent with customers getting the first month free, but being billable from the second up to the final month.
Demo
use make flag comparing join month and year with bill month and year and create a flag for each user then use sub-query for filter
select * from t
(
select *,case when (month(date_joined)=month(billdate)
and year(date_joined)=year(billdate)) then 'N' else 'Y' end as flag_billable
from user
) as t where t.flag_billable='Y'
For your query
DECLARE #billdate AS DATE = '2018-09-01';
select * from
(
SELECT * ,case when (month(date_joined)=month(#billdate)
and year(date_joined)=year(#billdate)) then 'N' else 'Y' end as flag_billable
FROM person
) t where t.flag_billable='Y'

sql I don't want to lose null values when I'm using group by

I'm trying to find which merch made visit a poi which is out of his route. In order to do this I need to find null values from vp.poiid however when I make group by I lost null values on that column.
.
.
.
(SELECT
vp.routedate AS Date,
me.MerchName,
me.id AS Merchid,
ov.poiid AS Done,
vp.poiid AS Planned,
CASE WHEN ov.poiid IS NULL THEN COUNT(ov.poiid) END AS notDone,
CASE WHEN vp.poiid IS NULL THEN COUNT(vp.poiid) END AS outOfRoute
FROM atb_visitplan vp
FULL JOIN OutVisitData ov ON (vp.poiid = ov.poiid
AND ov.commitdate = vp.routedate
AND vp.merchid = ov.merchid)
INNER JOIN atb_merchs me ON (vp.merchid = me.id)
WHERE me.id = 1
GROUP BY
vp.routedate,
me.MerchAd,
me.id,
ov.poiid,
vp.poiid
) a
.
.
.
when I run this subquery I get this view:
date name id done planned notdone outofroute
2013-11-06 00:00:00.000 Seven Kavak 1 30 30 0 NULL
2013-11-06 00:00:00.000 Seven Kavak 1 34 34 0 NULL
2013-11-06 00:00:00.000 Seven Kavak 1 48 48 0 NULL
2013-11-06 00:00:00.000 Seven Kavak 1 54 54 0 NULL
2013-11-06 00:00:00.000 Seven Kavak 1 NULL 1235 1235 NULL
2013-11-06 00:00:00.000 Seven Kavak 1 NULL 1236 1236 NULL
2013-11-07 00:00:00.000 Seven Kavak 1 30 30 0 NULL
2013-11-07 00:00:00.000 Seven Kavak 1 49 49 0 NULL
As you see in this result outOfRoute values all NULL (but it is actually not). Is there any way to see the actual results in this table? And I need to group these columns, this is a subquery and I count the final results. Planned column have null values actually but I cannot see them also.
PS: I'm using sql server
I think I misread the first time. You want a count of the records which have nulls for those, right?
COUNT(SELECT * FROM ov WHERE ov.poiid IS NULL) AS notDone,
COUNT(SELECT * FROM vp WHERE vp.poiid IS NULL) AS outOfRoute
Your current statements say, "Display a count of these records when the grouped field is null," which it looks is never.
You'll need to redefine the joins within those selects; I went for brevity for the moment.
(Rewritten with, I hope, better answer.)
your CASE WHEN vp.poiid IS NULL THEN COUNT(vp.poiid) END AS outOfRoute statement will count only when vp.poiid is null and you dont have else part. If i check the result all your vp.poiid AS Planned column is having values so obviously you will get null values in outOfRoute column.