Finding the the difference in day from previous record and partitioning by category - sql

I have the following table:
+----------+------------+----------------+
| Customer | Date | DesiredDayDiff |
+----------+------------+----------------+
| aaa | 12/09/2018 | 0 |
| aaa | 18/09/2018 | 6 |
| aaa | 25/09/2018 | 13 |
| aaa | 27/09/2018 | 15 |
| aaa | 28/09/2018 | 16 |
| bbb | 07/09/2018 | 0 |
| bbb | 11/09/2018 | 4 |
| bbb | 11/09/2018 | 4 |
+----------+------------+----------------+
I need to be able to calculate the difference of day from previous record, for that particular customer.
I believe there is added functionality in SQL server 2012+ that allows some sort of window functioning?? If this can be done using a window function, this would be a bonus as it hopefully, allows my query to be a lot more tidy.
I couldn't find a similar thread where the solution partitions by another category (in this instance above, it's the customer)

If I follow your narrative and diff from the previous row, LAG works for that:
declare #t table (Customer char(3), Date date, DesiredDayDiff int)
insert into #t(Customer,Date,DesiredDayDiff) values
('aaa','20180912',0),
('aaa','20180918',6),
('aaa','20180925',13),
('aaa','20180927',15),
('aaa','20180928',16),
('bbb','20180907',0),
('bbb','20180911',4),
('bbb','20180911',4)
select
*,
COALESCE(DATEDIFF(day,LAG(Date) OVER (PARTITION BY Customer ORDER By Date),Date),0)
from
#t
Results:
Customer Date DesiredDayDiff
-------- ---------- -------------- -----------
aaa 2018-09-12 0 0
aaa 2018-09-18 6 6
aaa 2018-09-25 13 7
aaa 2018-09-27 55 2
aaa 2018-09-28 66 1
bbb 2018-09-07 0 0
bbb 2018-09-11 4 4
bbb 2018-09-11 4 0
To match your "desired" column, I have to use FIRST_VALUE instead.

Related

SQL Server - Counting total number of days user had active contracts

I want to count the number of days while user had active contract based on table with start and end dates for each service contract. I want to count the time of any activity, no matter if the customer had 1 or 5 contracts active at same time.
+---------+-------------+------------+------------+
| USER_ID | CONTRACT_ID | START_DATE | END_DATE |
+---------+-------------+------------+------------+
| 1 | 14 | 18.02.2021 | 18.04.2022 |
| 1 | 13 | 02.01.2019 | 02.01.2020 |
| 1 | 12 | 01.01.2018 | 01.01.2019 |
| 1 | 11 | 13.02.2017 | 13.02.2019 |
| 2 | 23 | 19.06.2021 | 18.04.2022 |
| 2 | 22 | 01.07.2019 | 01.07.2020 |
| 2 | 21 | 19.01.2019 | 19.01.2020 |
+---------+-------------+------------+------------+
In result I want a table:
+---------+--------------------+
| USER_ID | DAYS_BEEING_ACTIVE |
+---------+--------------------+
| 1 | 1477 |
| 2 | 832 |
+---------+--------------------+
Where
1477 stands by 1053 (days from 13.02.2017 to 02.01.2020 - user had active contracts during this time) + 424 (days from 18.02.2021 to 18.04.2022)
832 stands by 529 (days from 19.01.2019 to 01.07.2020) + 303 (days from 19.06.2021 to 18.04.2022).
I tried some queries with joins, datediff's, case when conditions but nothing worked. I'll be grateful for any help.
If you don't have a Tally/Numbers table (highly recommended), you can use an ad-hoc tally/numbers table
Example or dbFiddle
Select User_ID
,Days = count(DISTINCT dateadd(DAY,N,Start_Date))
from YourTable A
Join ( Select Top 10000 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1, master..spt_values n2
) B
On N<=DateDiff(DAY,Start_Date,End_Date)
Group By User_ID
Results
User_ID Days
1 1477
2 832

How to join transactional data with customer data tables and perform case-based operations in SQL

I'm trying to perform a query between two different tables and come up with a case by case scenario, coming up with a list of records of calls for a specific month.
Here are my tables:
Customer table:
+----+----------------+------------+
| id | name | number |
+----+----------------+------------+
| 1 | John Doe | 8973221232 |
| 2 | American Dad | 7165531212 |
| 3 | Michael Clean | 8884731234 |
| 4 | Samuel Gatsby | 9197543321 |
| 5 | Mike Chat | 8794029819 |
+----+----------------+------------+
Transaction data:
+----------+------------+------------+----------+---------------------+
| trans_id | incoming | outgoing | duration | date_time |
+----------+------------+------------+----------+---------------------+
| 1 | 8973221232 | 9197543321 | 64 | 2018-03-09 01:08:09 |
| 2 | 3729920490 | 7651113929 | 276 | 2018-07-20 05:53:10 |
| 3 | 8884731234 | 8973221232 | 382 | 2018-05-02 13:12:13 |
| 4 | 8973221232 | 9234759208 | 127 | 2018-07-07 15:32:30 |
| 5 | 7165531212 | 9197543321 | 852 | 2018-08-02 07:40:23 |
| 6 | 8884731234 | 9833823023 | 774 | 2018-07-03 14:27:52 |
| 7 | 8273820928 | 2374987349 | 120 | 2018-07-06 05:27:44 |
| 8 | 8973221232 | 9197543321 | 79 | 2018-07-30 12:51:55 |
| 9 | 7165531212 | 7651113929 | 392 | 2018-05-22 02:27:38 |
| 10 | 5423541524 | 7165531212 | 100 | 2018-07-21 22:12:20 |
| 11 | 9197543321 | 2983479820 | 377 | 2018-07-20 17:46:36 |
| 12 | 8973221232 | 7651113929 | 234 | 2018-07-09 03:32:53 |
| 13 | 7165531212 | 2309483932 | 88 | 2018-07-16 16:22:21 |
| 14 | 8973221232 | 8884731234 | 90 | 2018-09-03 13:10:00 |
| 15 | 3820838290 | 2093482348 | 238 | 2018-04-12 21:59:01 |
+----------+------------+------------+----------+---------------------+
What am I trying to accomplish?
I'm trying to compile a list of "costs" for each of the customers that made calls on July 2018. The costs are based on:
1) If the customer received a call (incoming), the cost of the call is equal to the duration;
2) if the customer made a call (outgoing), the cost of the call is 100 if the call is 30 or less in duration. If it exceeds 30 duration, then the cost is 100 plus 5 * duration of the exceeded period.
If the customer didn't make any calls during that month he shouldn't be on the list.
Examples:
1) Customer American Dad has 3 incoming calls and 1 outgoing call, however only trans_id 10 and 13 are for the month of July. He should be paying a total of 538:
for trans_id 10 = 450 (100 for the first 30s + 5 * 70 for the remaining)
for trans_id 13 = 88
2) Customer Samuel Gatsby has 1 incoming call and 3 outgoing calls, however only trans_id 8 and 11 are for the month of July. He should be paying a total of 722:
for trans_id 8 = 345 (100 for the first 30s + 5 * 49 for the remaining)
for trans_id 11 = 377
Considering only these two examples, the output would be:
+----+----------------+------------+------------+
| id | name | number | billable |
+----+----------------+------------+------------+
| 2 | American Dad | 7165531212 | 538 |
| 4 | Samuel Gatsby | 9197543321 | 722 |
+----+----------------+------------+------------+
Note: Mike Chat shouldn't be on the list as he didn't make or receive any calls for that specific month.
What have I tried so far?
I've been playing cat and mouse with this one, I'm using the number as uniqueID, already attempted both a full outer join and combining where incoming or outgoing is not null then applying rules by case, tried doing a left join and applying cases, but I'm circling around and I can't get to a final list. Whenever I get incoming or outgoing, I'm either not able to apply the case or not able to come with both together. Really appreciate the help!
select customer_name.name, customer_name.number, bill = (CASE
WHEN customer_name.number = transaction_data.incoming then 'sum bill'
else 'multiply and add'
end)
from customer_name
left join transaction_data on customer_name.number = transaction_data.incoming or customer_name.name = transaction_data.outgoing
where strftime('%Y-%m', transaction_data.date_time) = '2018-07'
Note: I'm using sqlite to try it out online but the database is on SQL Server 2012, so I know that I can use a date format much easier, that way, but I'd like to keep as close to T-SQL as possible.
Also tried creating a case to determine whether it's incoming call or outgoing, but I'm only getting incoming as a result, even though trans_id 10 is outgoing:
select name, number, duration, case
when customer_name.number = transaction_data.incoming then 'incoming'
when customer_name.number = transaction_data.outgoing then 'outgoing'
END direction
from customer_name
left join transaction_data on customer_name.number = transaction_data.incoming or customer_name.name = transaction_data.outgoing
where strftime('%Y-%m', transaction_data.date_time) = '2018-07'
Try this:
SELECT
c."name", c.number,
SUM(CASE c.number
WHEN t.incoming THEN t.duration
ELSE IIF(t.duration - 30 < 0, 0, t.duration - 30) * 5 + 100
END) AS billable
FROM Customer AS c INNER JOIN [Transaction] AS t
ON c.number IN(t.incoming, t.outgoing)
WHERE t.date_time >= '20180701' AND t.date_time < '20180801'
GROUP BY c."name", c.number
Output:
| name | number | billable |
+---------------+------------+----------+
| John Doe | 8973221232 | 440 |
| American Dad | 7165531212 | 538 |
| Michael Clean | 8884731234 | 774 |
| Samuel Gatsby | 9197543321 | 722 |
Test it online with SQL Fiddle.

SQL Calculate daily increment with Date and ID on multiple variables

I have a table with the following structure:
+----------+-----+-------+----------+
| Date | ID | Likes | Comments |
+----------+-----+-------+----------+
| 1/1/2018 | AAA | 70 | 90 |
| 1/2/2018 | AAA | 80 | 110 |
| 1/1/2018 | BBB | 60 | 5 |
| 1/2/2018 | BBB | 90 | 6 |
+----------+-----+-------+----------+
For each day and ID, I need to calculate the incremental value. The desired output should something like this: desired output
+----------+-----+-------+----------+-------------+----------------+
| Date | ID | Likes | Comments | daily_likes | daily_comments |
+----------+-----+-------+----------+-------------+----------------+
| 1/1/2018 | AAA | 70 | 90 | 70 | 90 |
| 1/2/2018 | AAA | 80 | 110 | 10 | 20 |
| 1/1/2018 | BBB | 60 | 5 | 60 | 5 |
| 1/2/2018 | BBB | 90 | 6 | 30 | 1 |
+----------+-----+-------+----------+-------------+----------------+
I have tried this code but it keeps aggregating the daily_likes or daily_comments together.
SELECT
"date",
"created_time",
("likes_count" - LAG("likes_count", 1) OVER (ORDER BY "date")) AS "daily_likes",
("comments_count" - LAG("comments_count", 1) OVER (ORDER BY "date")) AS "daily_comments",
"id",
"likes_count",
"comments_count",
"user_username"
FROM
"blablabla"
GROUP BY
1,
"id",
"likes_count",
"comments_count",
"user_username",
"created_time"
ORDER BY
1 DESC;
The table structure you postet doesn't match the query. And I'm wondering why you group at all, let alone on nearly every column and a literal...
A query, that would produce the desired output you posted from a table like you posted would be:
SELECT "date",
"id",
"likes",
"comments",
"likes"
- lag("likes",
1,
0) OVER (PARTITION BY "id"
ORDER BY "date") "daily_likes",
"comments"
- lag("comments",
1,
0) OVER (PARTITION BY "id"
ORDER BY "date") "daily_comments"
FROM "elbat"
ORDER BY "date",
"id";
SQL Fiddle
There is no need to group at all.
(However note, to show the daily increase, this requires the source table to have data for every day for every user. If you got gaps, you'd need to join all days (in the respective range) for all users to fill them.

SQL Combine two tables with two parameters

I searched forum for 1h and didn't find nothing similar.
I have this problem: I want to compare two colums ID and DATE if they are the same in both tables i want to put number from table 2 next to it. But if it is not the same i want to fill yearly quota on the date. I am working in Access.
table1
id|date|state_on_date
1|30.12.2013|23
1|31.12.2013|25
1|1.1.2014|35
1|2.1.2014|12
2|30.12.2013|34
2|31.12.2013|65
2|1.1.2014|43
table2
id|date|year_quantity
1|31.12.2013|100
1|31.12.2014|150
2|31.12.2013|200
2|31.12.2014|300
I want to get:
table 3
id|date|state_on_date|year_quantity
1|30.12.2013|23|100
1|31.12.2013|25|100
1|1.1.2014|35|150
1|2.1.2014|12|150
2|30.12.2013|34|200
2|31.12.2013|65|200
2|1.1.2014|43|300
I tried joins and reading forums but didn't find solution.
Are you looking for this?
SELECT id, date, state_on_date,
(
SELECT TOP 1 year_quantity
FROM table2
WHERE id = t.id
AND date >= t.date
ORDER BY date
) AS year_quantity
FROM table1 t
Output:
| ID | DATE | STATE_ON_DATE | YEAR_QUANTITY |
|----|------------|---------------|---------------|
| 1 | 2013-12-30 | 23 | 100 |
| 1 | 2013-12-31 | 25 | 100 |
| 1 | 2014-01-01 | 35 | 150 |
| 1 | 2014-01-02 | 12 | 150 |
| 2 | 2013-12-30 | 34 | 200 |
| 2 | 2013-12-31 | 65 | 200 |
| 2 | 2014-01-01 | 43 | 300 |
Here is SQLFiddle demo It's for SQL Server but should work just fine in MS Accesss.

HOW TO: SQL Server select distinct field based on max value in other field

id tmpname date_used tkt_nr
---|---------|------------------|--------|
1 | template| 04/03/2009 16:10 | 00011 |
2 | templat1| 04/03/2009 16:11 | 00011 |
5 | templat2| 04/03/2009 16:12 | 00011 |
3 | diffname| 03/03/2009 15:11 | 00022 |
4 | diffname| 03/03/2009 16:12 | 00022 |
6 | another | 03/03/2009 16:13 | NULL |
7 | somethin| 24/12/2008 11:12 | 00023 |
8 | name | 01/01/2009 12:12 | 00026 |
I would like to have the result:
id tmpname date_used tkt_nr
---|---------|------------------|--------|
5 | templat2| 04/03/2009 16:12 | 00011 |
4 | diffname| 03/03/2009 16:12 | 00022 |
7 | somethin| 24/12/2008 11:12 | 00023 |
8 | name | 01/01/2009 12:12 | 00026 |
So what I'm looking for is to have distinct tkt_nr values excluding NULL, based on the max value of datetime.
I have tried several options but always failed
SELECT *
FROM templateFeedback a
JOIN (
SELECT ticket_number, MAX(date_used) date_used
FROM templateFeedback
GROUP BY ticket_number
) b
ON a.ticket_number = b.ticket_number AND a.date_used = b.date_used
I would appreciate any help. Unfortunately I need the code to be compatible with SQL Server.
I've stopped doing things this way since I discovered windowing functions. Too often, there are two records with the same timestamp and I get two records in the resultset. Here's the code for tSQL. Similar for Oracle. I don't think mySQL supports this yet.
Select id, tmpname, date_used, tkt_nbr
From
(
Select id, tmpname, date_used, tkt_nbr,
rownum = Row_Number() Over (Partition by tkt_nbr Order by date_used desc)
) x
Where row_num=1