How to insert values into the following table by incrementing the date column by 1 day for the next 23 days? - sql

I am using SQL Server 2014. I have a table names T1 (extract shown below):
Company ReviewDate Rank Type Reviews
...
A 2020-10-12 8 Hotel 675
B 2020-10-12 10 Hotel 450
C 2020-10-12 21 Hotel 89
D 2020-10-12 60 Hotel 1200
A 2020-10-13 5 Hotel 688
B 2020-10-13 10 Hotel 500
C 2020-10-13 21 Hotel 89
D 2020-10-13 60 Hotel 1200
I need to update table T1 for period 2020-10-13 to 2020-11-04 with the following logic: All rows to be appended to the table will have the same values as those for ReviewDate on 2020-10-12 except the ReviewDate which will increment by 1 day until 2020-11-04 is reached.
So here is an extract of how the final table will look like (only appended ReviewDate 2020-10-13 shown:
Company ReviewDate Rank Type Reviews
...
A 2020-10-11 8 Hotel 675
B 2020-10-11 10 Hotel 450
C 2020-10-11 21 Hotel 89
D 2020-10-11 60 Hotel 1200
A 2020-10-12 5 Hotel 688
B 2020-10-12 10 Hotel 500
C 2020-10-12 21 Hotel 89
D 2020-10-12 60 Hotel 1200
A 2020-10-13 5 Hotel 688
B 2020-10-13 10 Hotel 500
C 2020-10-13 21 Hotel 89
D 2020-10-13 60 Hotel 1200
...
NOTE: the table also contain entries with ReviewDate before 2020-10-12 but I just need to insert values into the table for a specific period with data related to 2020-10-12
How can I do that with a T-SQL query?

You need to insert rows. One method is to cross join to the dates you want and use that:
insert into t1 (Company, ReviewDate, Rank, Type, Reviews)
select t1.Company, v.date, t1.Rank, t1.Type, t1.Reviews
from t1 cross join
(values ('2020-10-13'), ('2020-10-14'), . . .
) v(date
where t1.reviewdate = '2020-10-12';
There are other ways to create the dates. One method is a recursive CTE:
with dates as (
select convert(date, '2020-10-13') as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < '2020-11-04'
)
select . . .
from t1 cross join
dates d;

Try this code.
DECLARE #TABLE TABLE (
COMPANY VARCHAR(1),
ReviewDate Date,
[Rank] int
)
INSERT INTO #TABLE VALUES ('A','2020-10-11',5)
INSERT INTO #TABLE VALUES ('B','2020-10-11',10)
INSERT INTO #TABLE VALUES ('C','2020-10-11',21)
INSERT INTO #TABLE VALUES ('D','2020-10-11',60)
;WITH CTE AS (
SELECT * FROM #TABLE
UNION ALL
SELECT
COMPANY,
dateadd(day,1,ReviewDate),
[Rank]
from CTE
WHERE ReviewDate <= '2020-10-13'
)
SELECT * FROM CTE
ORDER BY 2,1

Related

T-SQL get values for specific group

I have a table EmployeeContract similar like this:
ContractId
EmployeeId
ValidFrom
ValidTo
Salary
12
5
2018-02-01
2019-06-31
x
25
8
2015-01-01
2099-12-31
x
50
5
2019-07-01
2021-05-31
x
52
6
2011-08-01
2021-12-31
x
72
8
2010-08-01
2014-12-31
x
52
6
2011-08-01
2021-12-31
x
Table includes history contracts in company for each employee. I need to get date when employees started work and last date of contract. Sometime records has duplicates.
For example, based on data from above:
EmployeeId
ValidFrom
ValidTo
5
2018-02-01
2021-05-31
8
2010-08-01
2099-12-31
6
2011-08-01
2021-12-31
Base on this article: https://www.techcoil.com/blog/sql-statement-for-selecting-the-latest-record-in-each-group/
I prepared query like this:
select minv.*, maxv.maxvalidto from
(select distinct con.[EmployeeId], mvt.maxvalidto
from [EmployeeContract] con
join (select [EmployeeId], max(validto) as maxvalidto
FROM [EmployeeContract]
group by [EmployeeId]) mvt
on con.[EmployeeId] = mvt.[EmployeeId] and mvt.maxvalidto = con.validto) maxv
join
(select distinct con.[EmployeeId], mvf.minvalidfrom
from [EmployeeContract] con
join (select [EmployeeId], min(validfrom) as minvalidfrom
FROM [EmployeeContract]
group by [EmployeeId]) mvf
on con.[EmployeeId] = mvf.[EmployeeId] and mvf.minvalidfrom = con.validfrom) minv
on minv.[EmployeeId] = maxv.[EmployeeId]
order by 1
But I'm not satisfied, i think it's not easy to read, and probably optimize is poor. How can I do it better?
I think you want group by:
select employeeid, min(validfrom), max(validto)
from employeecontract
group by employeeid

Query that'll identify returning active users in span of week

Write a query that'll identify returning active users. A returning active user is a user that has made a second purchase within 7 days of their first purchase.
id u_id item created_at revenue
1 109 milk 3/3/2020 0:00 123
2 139 biscuit 3/18/2020 0:00 421
3 120 milk 3/18/2020 0:00 176
4 108 banana 3/18/2020 0:00 862
5 130 milk 3/28/2020 0:00 333
6 103 bread 3/29/2020 0:00 862
7 122 banana 3/7/2020 0:00 952
8 125 bread 3/13/2020 0:00 317
9 139 bread 3/23/2020 0:00 929
10 141 banana 3/17/2020 0:00 812
11 116 bread 3/31/2020 0:00 226
12 128 bread 3/4/2020 0:00 112
13 146 biscuit 3/4/2020 0:00 362
14 119 banana 3/28/2020 0:00 127
You can use window functions to get the earliest creation date and then look for other records within one week:
select distinct u_id
from (select t.*,
min(created_at) over (partition by u_id) as min_created_at
from t
) t
where created_at > min_created_at and
created_at < min_created_at + interval 7 day;
If you just check the first time the customer purchased, and a second visit in the next 7 days, you will discard a third purchase after the second visit.
just globally check two purchases in a 7 day interval like this:
create table t(id integer, u_id integer, item varchar(100),created_at date,revenue float);
insert into t
values (1, 109, "milk" , STR_TO_DATE("3/3/2020", '%m/%d/%Y') , 123)
, (2,139,"biscuit",STR_TO_DATE("3/18/2020", '%m/%d/%Y'),421)
, (3,120,"milk",STR_TO_DATE("3/18/2020", '%m/%d/%Y'),176)
, (4,108,"banana",STR_TO_DATE("3/18/2020", '%m/%d/%Y'),862)
, (5,130,"milk",STR_TO_DATE("3/28/2020", '%m/%d/%Y'),333)
, (6,103,"bread",STR_TO_DATE("3/29/2020", '%m/%d/%Y'),862)
, (7,122,"banana",STR_TO_DATE("3/7/2020", '%m/%d/%Y'),952)
, (8,125,"bread",STR_TO_DATE("3/13/2020", '%m/%d/%Y'),317)
, (9,139,"bread",STR_TO_DATE("3/23/2020", '%m/%d/%Y'),929)
, (10,141,"banana",STR_TO_DATE("3/17/2020", '%m/%d/%Y'),812)
, (11,116,"bread",STR_TO_DATE("3/31/2020", '%m/%d/%Y'),226)
, (12,128,"bread",STR_TO_DATE("3/4/2020", '%m/%d/%Y'),112)
, (13,146,"biscuit",STR_TO_DATE("3/4/2020", '%m/%d/%Y'),362)
, (14,119,"banana",STR_TO_DATE("3/28/2020", '%m/%d/%Y'),127);
select * from t as t1 where exists (select * from t as t2 where t1.u_id = t2.u_id and t1.created_at - t2.created_at > 0 and t1.created_at - t2.created_at <= 7 );
The above example solved using window functions. You need to use date casting at the where condition since the given table in datetime format. This is tricky part to handle.
create table public.purchase_history(
id int,
userid int,
item varchar,
created_at datetime,
revenue int);
Insert into public.purchase_history values
(1, 109, 'milk' ,'03/03/2020' , 123)
, (2,139,'biscuit','03/18/2020',421)
, (3,120,'milk','03/18/2020',176)
, (4,108,'banana','03/18/2020',862)
, (5,130,'milk','03/28/2020',333)
, (6,103,'bread','03/29/2020',862)
, (7,122,'banana','03/07/2020',952)
, (8,125,'bread','03/13/2020',317)
, (9,139,'bread','03/23/2020',929)
, (10,141,'banana','03/17/2020',812)
, (11,116,'bread','03/31/2020',226)
, (12,128,'bread','03/04/2020',112)
, (13,146,'biscuit','03/04/2020',362)
, (14,119,'banana','03/28/2020',127)
, (15,120,'milk','03/28/2020',186);
select distinct userid
FROM
(select
id,
userid,
created_at,
coalesce(lead(created_at)over(partition by userid order by created_at),'9999-12-31') as next_purchase
from
public.purchase_history order by userid)repeated
where (repeated.next_purchase::date-repeated.created_at::date)<=7;
Let's say the tables name is amazon_transactions
SELECT distinct a.user_id FROM amazon_transactions a
JOIN amazon_transactions b
ON a.user_id = b.user_id
WHERE a.id <>b.id
AND a.created_at <= b.created_at
AND b.created_at <= a.created_at+7
ORDER BY a.user_id

Managment Studio Query

Wasn't sure what title to give this. I'm currently writing a query using the table below in SQL Server Management Studio 2012.
If an employee has Qualification 'BDE' then that supersedes Qualification 'RVT' and 'RVT' doesn't need to show in my output for EmpID 1. It does need to show for EMP2 because they don't have Qualification 'BDE'
I've tried various ways using Union and a row partition, I'm sure this isn't a particularly tough request but I'm running out of ideas
EmpID Department ObtainedDate ExpiryDate Qualification DaystoExpire
1 HR 2019-06-12 2024-06-12 BDE 1819
1 HR 2017-06-09 2021-09-18 FGA 821
1 HR 2019-06-18 2021-09-18 RVT 821
1 HR 2019-01-28 2020-01-28 HIJ 222
1 HR 2019-06-03 2019-07-03 TTT 13
2 payroll 2018-10-18 2019-10-18 RVT 120
What I would like
EmpID Department ObtainedDate ExpiryDate Qualification DaystoExpire
1 HR 2019-06-12 2024-06-12 BDE 1819
1 HR 2017-06-09 2021-09-18 FGA 821
1 HR 2019-01-28 2020-01-28 HIJ 222
1 HR 2019-06-03 2019-07-03 TTT 13
2 payroll 2018-10-18 2019-10-18 RVT 120
I think this is what you want, I have not taken all the columns as you have mentioned in your example.
As per your given data, this should work:
Declare #t table (EMPID int ,Department varchar(50),obtaineddate varchar(50),ExpiryDate varchar(50),Qualification varchar(50),DaystoExpire INT)
insert into #t values (1,'HR','2019-06-12','2024-06-12','BDE',1819),
(1,'HR','2017-06-09','2021-09-18','FGA',821),
(1,'HR','2019-06-18','2021-09-18','RVT',821),
(1,'HR','2019-01-28','2020-01-28','HIJ',222),
(1,'HR','2019-06-03','2019-07-03','TTT',13),
(2,'Payroll','2018-10-18','2019-10-18','RVT',120)
select * from #t a
where not exists ( select * from #t where a.EMPID=EMPID and a.Qualification='RVT')
UNION
select * from #t a
where exists ( select * from #t where a.EMPID=EMPID and Qualification='RVT')
and not exists (select * from #t where a.EMPID=EMPID and Qualification='BDE')
order by EMPID,Qualification

I am attempting to sum two tables within themselves and then subtract the one table from the other. h

I am attempting to sum two tables together and subtract the one from the other. With a query, I can sum the two tables together, but am not able to subtract them. This is being attempted in Postgres Here are the tables:
memberid ticker numshares purchasedate transactionid purchaseprice
7 aapl 1 Jun 28, 2017 22 146
7 aapl 1 Jun 28, 2017 23 146
7 hog 1 Jun 28, 2017 24 55
7 aapl 10 Jun 28, 2017 25 1458
7 aapl 2 Jun 28, 2017 26 292
7 aapl 3 Jun 28, 2017 27 437
7 aapl 5 Jun 28, 2017 28 729
Here is the sells table:
memberid ticker numshares selldate sellid sellprice
7 aapl 10 Jun 28, 2017 4 1458
7 aapl 15 Jun 29, 2017 5 2154
UPDATE: I got everything to work except one detail with the following
Postgres statement:
`select qbuys.ticker, qbuys.total_purchaseprice-qsells.total_sellprice
as current_total, qbuys.total_purchaseshares-qsells.total_sellshares as
current_shares
from
(select sells.ticker, sum(numshares) as total_sellshares,
sum(sellprice) as total_sellprice
from sells
where memberid = 7
group by ticker) as qsells, (select stocks.ticker, sum(numshares) as
total_purchaseshares, sum(purchaseprice) as total_purchaseprice from
stocks
where memberid = 7
group by ticker) as qbuys
where qbuys.ticker=qsells.ticker`
The only issue remaining is that this does not return purchases that do not have a corresponding sell. It only spits out the total if there is a buy and a sell of the same ticker in each table. So the update I need is to have this spit out purchases that do not have a corresponding sell.
You need to join the two tables in order to subtract the sum of one column from the other.
SELECT
a.ticker
,sum(a.numshares) as Total_Sell_Shares
,sum(a.sellprice) as Total_Sell_Price
,sum(a.numshares) - sum(b.numshares) as Total_Shares
,sum(a.sellprice) - sum(b.purchaseprice) as Total_Price
FROM Sells A
JOIN Stocks B on a.ticker = b.ticker
GROUP BY a.ticker
First, a sample schema w/ data (in postgres):
derek=# create table t1 (ticker varchar, numshares integer);
CREATE TABLE
derek=# create table t2 (ticker varchar, numshares integer);
CREATE TABLE
derek=# insert into t1 values ('aapl',10);
INSERT 0 1
derek=# insert into t1 values ('aapl',5);
INSERT 0 1
derek=# insert into t2 values ('aapl',3);
INSERT 0 1
Then the trick is to use inner queries for each aggregate function:
derek=# select t1a.ticker, buys, sells, buys-sells as position
from (select ticker, sum(numshares) as buys from t1 group by ticker) as t1a
join (select ticker, sum(numshares) as sells from t2 group by ticker) as t2a
on t1a.ticker=t2a.ticker;
ticker | buys | sells | position
--------+------+-------+----------
aapl | 15 | 3 | 12
(1 row)
I would try something like this. What do you think?
select stocks.ticker, sum(stocks.numshares) - sum(sells.numshares) as total_shares,
sum(stocks.purchaseprice) - sum(sells.sellprice) as total_price
from stocks
left join sells
on stocks.ticker = sells.ticket
where stocks.memberid = 7
group by stocks.ticker;

SQL Crosstab with undetermined columns

I see a lot of similar questions, but almost all of them wind up grouping results as column names (Column names based on results), mine is a more simple list. I don't care if it uses dynamic SQL or not (I'd think it has to).
Please don't tell me I need to restructure the tables, I'm working from a legacy system and don't have that option.
Basically, I just need a list of all valid table "B" entries that match a given record from table "A", in a row.
I don't have any code sample yet, because I'm not seeing a way to set this up correctly.
Table: Customer c
CustID Name
1 Bill Smith
2 Jim Jones
3 Mary Adams
4 Wendy Williams
Table: Debt d
CustID Creditor Balance
1 ABC Loans 245
1 Citibank 815
2 Soprano Financial 74000
3 Citibank 24
3 Soprano Financial 93000
3 Wells Fargo 275
3 Midwestern S&L 2500
4 ABC Loans 1500
4 Fred's Payday Loan 1000
Desired Output:
Name Cred1 Bal1 Cred2 Bal2 Cred3 Bal3 Cred4 Bal4
Bill Smith ABC Loans 245 Citibank 815 (NULL) (NULL) (NULL) (NULL)
Jim Jones Soprano Financial 74000 (NULL) (NULL) (NULL) (NULL) (NULL) (NULL)
Mary Adams Citibank 24 Soprano Finanacial 93000 Wells Fargo 275 Midwestern S&L 2500
Wendy Williams ABC Loans 1500 Fred's Payday Loan 1000 (NULL) (NULL) (NULL) (NULL)
Basically, I probably have to collect some kind of count of the most number of records for any specific "CustomerID", and define the output columns based on that. If this has already been answered, feel free to link and close this out, I did not see this specific scenario when I did my search.
Here is another dynamic approach. We use Row_Number() to create the minimal number of columns.
Example
Declare #SQL varchar(max) = Stuff((Select Distinct ','+QuoteName(concat('Cred',ColNr))
+','+QuoteName(concat('Bal',ColNr))
From (Select ColNr=Row_Number() over (Partition By CustID Order By Creditor) From Debt ) A
Order By 1
For XML Path('')),1,1,'')
Select #SQL = '
Select *
From (
Select C.Name
,B.*
From (
Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
From Debt
) A
Cross Apply (values (concat(''Cred'',ColNr),[Creditor])
,(concat(''Bal'' ,ColNr) ,cast(Balance as varchar(25)))
) B (Item,Value)
Join Customer C on A.CustID=C.CustID
) A
Pivot (max([Value]) For [Item] in (' + #SQL + ') ) p'
--Print #SQL
Exec(#SQL);
Returns
If if Helps, the Generated SQL Looks Like This:
Select *
From (
Select C.Name
,B.*
From (
Select *,ColNr=Row_Number() over (Partition By CustID Order By Creditor)
From Debt
) A
Cross Apply (values (concat('Cred',ColNr),[Creditor])
,(concat('Bal' ,ColNr) ,cast(Balance as varchar(25)))
) B (Item,Value)
Join Customer C on A.CustID=C.CustID
) A
Pivot (max([Value]) For [Item] in ([Cred1],[Bal1],[Cred2],[Bal2],[Cred3],[Bal3],[Cred4],[Bal4]) ) p
Just for the Visualization, the query "feeding" the Pivot generates:
I will guess you are already know how to use cross tab so you only need to prepare your data to use it.
STEP 1: Join both tables:
SELECT c.Name, d.Creditor, d.Balance
FROM Customer c
JOIN Debt d
ON c.CustID = d.CustID
STEP 2: Include a row number to each element related to the customer you are going to use to cross tab
SELECT c.Name, d.Creditor, d.Balance,
ROW_NUMBER() over (PARTITION BY Name ORDER BY creditor) as rndebt_tab
FROM Customer c
JOIN Debt d
ON c.CustID = d.CustID
Now you have:
CustID Creditor Balance rn
1 ABC Loans 245 1
1 Citibank 815 2
2 Soprano Financial 74000 1
3 Citibank 24 1
3 Soprano Financial 93000 2
3 Wells Fargo 275 3
3 Midwestern S&L 2500 4
4 ABC Loans 1500 1
4 Fred's Payday Loan 1000 2
STEP 3: Create the SOURCE for the cross tab
WITH cte as (
<query from step2>
)
SELECT Name,
'CREDITOR_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
Creditor as Value
FROM cte
UNION all
SELECT Name,
'DEBT_' + RIGHT('000' + CAST(rn AS VARCHAR(3)),3) as cross_tab,
CAST(Balance as VARCHAR(max)) as Value
FROM cte
Now you have:
CustID cross_tab Value
1 CREDITOR_001 ABC Loans
1 CREDITOR_002 Citibank
2 CREDITOR_001 Soprano Financial
3 CREDITOR_001 Citibank
3 CREDITOR_002 Soprano Financial
3 CREDITOR_003 Wells Fargo
3 CREDITOR_004 Midwestern S&L
4 CREDITOR_001 ABC Loans
4 CREDITOR_002 Fred's Payday Loan
1 DEBT_001 245
1 DEBT_002 815
2 DEBT_001 ` 74000
3 DEBT_001 24
3 DEBT_002 93000
3 DEBT_003 275
3 DEBT_004 2500
4 DEBT_001 1500
4 DEBT_002 1000
EDIT: I use CustID instead of Name on the example but too lazy to change now.