SQL Select Where date in (Jan and March) but not in Feb - sql

I have a table like this in SQL called Balance
+----+-----------+-------+------+
| id | accountId | Date | Type |
+----+-----------+-------+------+
| PK | FK | Date | Int |
+----+-----------+-------+------+
I need to find the accountIds that has balance entries in January and March, but not in Febuary.
Only in 2018 and Type should be 2.
How would I go about writing my sql select statement?
Thanks
Edit:
What's I've done so far:
Selecting rows that either in Jan OR March is not a problem for me.
SELECT AccountId, Date FROM Balance
WHERE Month(Date) in (1,3) AND YEAR(Date) = 2018 AND Type =2
ORDER BY AccountId, Date
But if an AccountId has a single entry, say in January, then this will be included. And that's not what I want.
Only if an Account has entries in both Jan and March, and not in Feb is it interesting.
I suspect Group BY and HAVING are keys here, but I'm unsure how to proceed

I would do this using aggregation:
select b.accountid
from balance b
where date >= '2018-01-01' and date < '2019-01-01'
group by b.accountid
having sum(case when month(date) = 1 then 1 else 0 end) > 0 and -- has january
sum(case when month(date) = 3 then 1 else 0 end) > 0 and -- has march
sum(case when month(date) = 2 then 1 else 0 end) = 0 -- does not have february

Related

Find repeat customers after making their transcation in a month

I've a sample data - data1
Date User Orderid
12-02-2020 A 50274
13-02-2020 B 34704
18-02-2020 A 12079
01-03-2020 C 69711
13-03-2020 B 36813
01-04-2020 D 57321
Customer A made the first transaction in Feb and another transaction in same month.
Customer B made the first transaction in Feb and made a transaction again in March.
How can I identify the customer acquisation in a month and their following months orders?
month | customers_acquired | made_transcation_in_month+1 | made_transaction_in_month+2
2 2 1 0
3 1 0 0
4 1 0 0
In the above result, In month 2, two customers made their first transcations and one of them made again in next month.
In month 3, one new customer made a transcation and never made any transactions again. Same goes with month 4.
select year(date) as "year"
,month(Date) as "month"
,count(new_customer_cnt) as customers_acquired
,count("repeat_customers+1") as "made_transcation_in_month+1"
,count("repeat_customers+2") as "made_transcation_in_month+2"
from (
select *
,case when "User" <> lag("User") over(order by "User", Date) or lag("User") over(order by "User", Date) is null then 1 end as new_customer_cnt
,case when "User" = lead("User") over(partition by "User" order by Date) and month(dateadd(month, 1, date)) = lead(month(date)) over(partition by "User" order by Date) then 1 end as "repeat_customers+1"
,case when "User" = lead("User") over(partition by "User" order by Date) and month(dateadd(month, 2, date)) = lead(month(date)) over(partition by "User" order by Date) then 1 end as "repeat_customers+2"
from t
) t
group by year(date), month(Date)
year
month
customers_acquired
made_transcation_in_month+1
made_transcation_in_month+2
2020
2
2
1
0
2020
3
1
0
0
2020
4
1
0
0
Fiddle

How to get data from date

Hi I would like to get data from date for users. I ve got a table with all months but i would like to get how much they earn on month
user
month
money
1
january
10
2
january
1
1
april
100
2
april
1000
1
march
0
2
march
1
And result should be:
user
money_on_april
money_on_march
1
100
0
2
1000
1
3
0
0
Assuming you want a column for every month, or a certain subset of months:
SELECT
user,
SUM(CASE month WHEN 'january' THEN money ELSE 0 END) As money_on_january,
SUM(CASE month WHEN 'february' THEN money ELSE 0 END) As money_on_february,
...
FROM
YourTable
GROUP BY
user
If you only want columns for the months which exist in the table, then you'll need to use dynamic SQL instead.
If you are using MS SQL, Try PIVOT
SELECT * FROM [Your Table]
PIVOT(
SUM([money])
FOR [month] IN ([january],[april],[march])
)pvt

SQL query to get running total for records matching condition over last 12 months

I am trying to query a table and get a running total for each of the last 12 months. A record could fall in more than one month if the range of two date fields falls on multiple months. The fields are DueDate and DeferralDate.
So for example, lets say I have the following 4 records:
Id | Date1 | Date2
1 01/20/2020 05/29/2020
2 02/01/2020 08/14/2020
3 04/01/2020 04/30/2020
4 07/08/2020 12/31/2020
My result would look like this:
Nov 19 | Dec 19 | Jan 20 | Feb 20 | Mar 20 | Apr 20 | May 20 | Jun 20 | Jul 20 | Aug 20 | Sept 20 | Oct 20
0 0 1 2 2 3 2 1 2 2 1 1
I have no idea how to go about this other than 12 separate queries but there's probably a better way to do it I'm unaware of. Hopefully someone can point me in the right direction.
Thanks in advance.
If you want this in columns, then it is conditional aggregation. Assuming you want any overlap in the month:
select sum(case when date1 < '2019-12-01' and date2 >= '20190-11-01' then 1 else 0 end) as cnt_201911,
sum(case when date1 < '2020-01-01' and date2 >= '20190-12-01' then 1 else 0 end) as cnt_201912,
sum(case when date1 < '2020-02-01' and date2 >= '2020-01-01' then 1 else 0 end) as cnt_202001,
sum(case when date1 < '2020-03-01' and date2 >= '2020-02-01' then 1 else 0 end) as cnt_202002,
. . .
from t
Select sum(count(date1) , count(date2)) ,
Format(date1,'MMMyy')
from tablename
Where month (date1) = month (date2)
Then you have to use Pivot to horizontalize the select result

SQL Calculations for budgeting

I have a database that contains the following columns:
Vendor, Amount, StartDate, Months
I would like to be able to calculate the average monthly amount based on the Months that are entered. I would also like to see it calculate out from the start date to the end date based on the StartDate + Months calculation. The resulting table would look something like this:
Vendor1 has 2 months of 1112 starting Jan 1 while Vendor2 has 3 months of 2040 staring Feb 1
| | ANNUAL | JAN | FEB | MAR | APR |
Vendor1 | 2,224 | 1,112 | 1,112 | | |
Vendor2 | 6,120 | | 2,040 | 2,040 | 2,040 |
Any assistance or direction would be greatly appreciated.
That's a strange DB design. However, here's what you've got to try:
SELECT (Amount * Months) AS Annual, (Case #(StartDate < DATE("01.02.year")) WHEN 1 THEN Amount ELSE NULL) AS Jan FROM Table --etc for all months
Will think of modifications though, because this way is a little too straightforward.
You would use conditional aggregation. Assuming the start dates are all in the same year, the code might look like this:
select vendorid, (amount * months) as total,
(case when month(startdate) <= 1 and month(startdate) + months >= 1
then amount
end) as jan,
(case when month(startdate) <= 2 and month(startdate) + months >= 2
then amount
end) as feb,
(case when month(startdate) <= 3 and month(startdate) + months >= 3
then amount
end) as mar,
(case when month(startdate) <= 4 and month(startdate) + months >= 4
then amount
end) as apr,
from t;

Combining two T-SQL pivot queries in one

Suppose you had this table:
CREATE TABLE Records
(
RecordId int IDENTITY(1,1) NOT NULL,
CreateDate datetime NOT NULL,
IsSpecial bit NOT NULL
CONSTRAINT PK_Records PRIMARY KEY(RecordId)
)
Now a report needs to be created where the total records and the total special records are broken down by month. I can use these two queries separately:
-- TOTAL RECORDS PER MONTH
SELECT January, February, March, April, May, June,
July, August, September, October, November, December
FROM (
SELECT RecordId, DATENAME(MONTH, CreateDate) AS RecordMonth
FROM dbo.Records
) AS SourceTable
PIVOT (
COUNT(RecordId) FOR RecordMonth IN (January, February, March, April, May, June,
July, August, September, October, November, December)
) AS PivotTable;
-- TOTAL SPECIAL RECORDS PER MONTH
SELECT January, February, March, April, May, June,
July, August, September, October, November, December
FROM (
SELECT RecordId, DATENAME(MONTH, CreateDate) AS RecordMonth
FROM dbo.Records
WHERE IsSpecial = 1
) AS SourceTable
PIVOT (
COUNT(RecordId) FOR RecordMonth IN (January, February, March, April, May, June,
July, August, September, October, November, December)
) AS PivotTable;
The results might look like this:
Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
total 0 0 2 2 1 0 0 1 2 1 2 4
total special 0 0 1 0 1 0 0 0 0 0 0 2
Is it possible to combine these two queries into a single more efficient query?
I would do it like this:
SELECT
CASE SQ.total_type
WHEN 1 THEN 'total special'
WHEN 2 THEN 'total expensive'
ELSE 'total'
END AS total_type,
SUM(CASE WHEN MONTH(R.CreateDate) = 1 THEN 1 ELSE 0 END) AS January,
SUM(CASE WHEN MONTH(R.CreateDate) = 2 THEN 1 ELSE 0 END) AS February,
SUM(CASE WHEN MONTH(R.CreateDate) = 3 THEN 1 ELSE 0 END) AS March,
...
FROM
dbo.Records R
INNER JOIN
(
SELECT 0 AS total_type UNION ALL -- All
SELECT 1 UNION ALL -- IsSpecial
SELECT 2 -- IsExpensive
) AS SQ ON
(R.IsSpecial | (R.IsExpensive * 2)) & SQ.total_type = SQ.total_type
GROUP BY
SQ.total_type
ORDER BY
SQ.total_type DESC
You can only have one aggregate (COUNT(RecordId)) per pivot so all you do is combine into one result set with a UNION ALL with a suitable extra column to identify each pivot.
Otherwise, you have no way to distinguish the 2 different aggregates in the pivot
Thanks for the solution Tom, that answers my pivot question.
Too bad for me I had the wrong question. For my problem I'm now feeling it would be better to use a plain grouping query like this instead:
SELECT DATENAME(MONTH, CreateDate) AS Month,
COUNT(*) AS Total,
SUM(CASE
WHEN IsSpecial = 1 THEN 1
ELSE 0
END) AS TotalSpecial,
SUM(CASE
WHEN IsExpensive = 1 THEN 1
ELSE 0
END) AS TotalExpensive
FROM Records
GROUP BY DATENAME(MONTH, CreateDate);
Then all that is left to do is rotate the results before they are presented. Nice to know eh?