SQL - Copy row multiple times changing date field each time - sql

I need to be able to copy a row of data multiple times and update a date field with each copy. So if the row that needs to be copied has a date of say 2022-12-01, I need that to be copied until the date field contains 2022-10-15 (can be any start/end dates depending upon what needs to be copied). This is something that is currently being done in Excel and then copy/pasted into a table but I would prefer to do it in SQL if possible.
Thanks in advance for any help you can provide.
I have not tried anything because I honestly don't know where to start.
Update - I was just given some code by a co-worker that is used to copy a row of data and insert with a different date. Can this be modified to be some sort of loop to go through a range of dates and employee ids?
Insert INTO tblUsers2
                    (ExtractDate, Username, EmployeeID, LastName, FirstName, MiddleInitial, Suffix, StartDate, EndDate, IsSupervisor, IsTeamLead, Department, Dept_Desc, 
                      UserLocation, CP_NCP, Account, AccountGroup, AccountOrganization, Supervisor, Level8, Level7, Level6, Level5, SVPName, Level3, Level2, Level1, JobTitle, 
                      GenesysLogon, Email, CreateDate, EmployeeDeptID, CeridianDate, Work_Phone, Home_Phone, Mobile_Phone, SeniorityDate, Location, TZSTDName, TZDisplayName,
                      Last_Hire_Date, Original_Hire_Date, Benefit_Calc_Date, MiddleName, GaxEmployeeID, WFO, samaccountname, Assigned_Role)
select     '2023-1-18 00:00:00.000' as extractdate, Username, EmployeeID, LastName, FirstName, MiddleInitial, Suffix, StartDate, EndDate, IsSupervisor, IsTeamLead, Department, Dept_Desc, 
                      UserLocation, CP_NCP, Account, AccountGroup, AccountOrganization, Supervisor, Level8, Level7, Level6, Level5, SVPName, Level3, Level2, Level1, JobTitle, 
                      GenesysLogon, Email, CreateDate, EmployeeDeptID, CeridianDate, Work_Phone, Home_Phone, Mobile_Phone, SeniorityDate, Location, TZSTDName, TZDisplayName,
                      Last_Hire_Date, Original_Hire_Date, Benefit_Calc_Date, MiddleName, GaxEmployeeID, WFO, samaccountname, Assigned_Role
FROM         CapacityPlanning.dbo.tblusers2
where ExtractDate = '1/19/2023' and employeeID in ('1019464','1019499','1019520','1016615','1019454','1019482');

Try this: CROSS JOIN with an in-line table consisting of consecutive integers, starting from 0:
CREATE TABLE startrow (dt DATE, other_col VARCHAR(20));
INSERT INTO startrow
SELECT
'2022-12-01'
,'other column data'
;
WITH
i(i) AS (
SELECT 0
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
)
SELECT
DATEADD(dd,i,dt) AS dt
, other_col
FROM startrow CROSS JOIN i
ORDER BY 1;
-- out dt | other_col
-- out ------------+-------------------
-- out 2022-12-01 | other column data
-- out 2022-12-02 | other column data
-- out 2022-12-03 | other column data
-- out 2022-12-04 | other column data
-- out 2022-12-05 | other column data
-- out 2022-12-06 | other column data
-- out 2022-12-07 | other column data
-- out 2022-12-08 | other column data
-- out 2022-12-09 | other column data
-- out 2022-12-10 | other column data

Thanks to everyone's input, I was able to come up with the following:
``
DECLARE #StartDate AS DATE = '2022-10-01 00:00:00.000'
DECLARE #EndDate AS DATE = '2022-09-07 00:00:00.000'
WHILE #StartDate >= #EndDate
BEGIN
Insert INTO tblUsers2
                    (ExtractDate, Username, EmployeeID, LastName, FirstName, MiddleInitial, Suffix, StartDate, EndDate, IsSupervisor, IsTeamLead, Department, Dept_Desc, 
                      UserLocation, CP_NCP, Account, AccountGroup, AccountOrganization, Supervisor, Level8, Level7, Level6, Level5, SVPName, Level3, Level2, Level1, JobTitle, 
                      GenesysLogon, Email, CreateDate, EmployeeDeptID, CeridianDate, Work_Phone, Home_Phone, Mobile_Phone, SeniorityDate, Location, TZSTDName, TZDisplayName,
                      Last_Hire_Date, Original_Hire_Date, Benefit_Calc_Date, MiddleName, GaxEmployeeID, WFO, samaccountname, Assigned_Role)
SELECT #StartDate AS extractdate, Username, EmployeeID, LastName, FirstName, MiddleInitial, Suffix, StartDate, EndDate, IsSupervisor, IsTeamLead, Department, Dept_Desc, 
                      UserLocation, CP_NCP, Account, AccountGroup, AccountOrganization, Supervisor, Level8, Level7, Level6, Level5, SVPName, Level3, Level2, Level1, JobTitle, 
                      GenesysLogon, Email, CreateDate, EmployeeDeptID, CeridianDate, Work_Phone, Home_Phone, Mobile_Phone, SeniorityDate, Location, TZSTDName, TZDisplayName,
                      Last_Hire_Date, Original_Hire_Date, Benefit_Calc_Date, MiddleName, GaxEmployeeID, WFO, samaccountname, Assigned_Role
FROM         CapacityPlanning.dbo.tblusers2
WHERE ExtractDate = '9/06/2022' and employeeID in ('118093','1021528')
SET #StartDate = CAST(#StartDate AS DATETIME) - 1;
END;
``

Related

CTE with paging returning random value from subquery

I have a table with contact(contacts) information and another relationship table with key/value data for the contact table (customdata), the custom data rows are not unique, they can repeat, and they have a creation date as well.
I have a CTE querying the contacts, pretty simple, but I also want to return subquery column with from particular key, and the value from this query happens to contain a date, stored as varchar, and since this table does not contain unique rows I'm using TOP 1 and sorting by the row creation date.
The issue I'm having is that the value from the custom data table is returning random values and to top it off its not sorting correctly when casted to date.
WITH Customers_CTE AS(
SELECT row_number() over (ORDER BY (SELECT TOP 1 CONVERT(DATETIME, data_value, 101)
FROM CustomData
WHERE (data_cust_id = Customers.cust_id AND data_key = 'Sign Date' AND ISDATE(data_value) = 1) ORDER BY data_created DESC) DESC) AS rowNum,
COUNT(*) OVER (PARTITION BY NULL) AS [RowCount],
Customers.FirstName,
Customers.LastName,
(SELECT TOP 1 CONVERT(DATETIME, data_value, 101)
FROM CustomData
WHERE (data_cust_id = Customers.cust_id AND data_key = 'Sign Date' AND ISDATE(data_value) = 1) ORDER BY data_created DESC) AS DateSigned
FROM Customers)
SELECT * FROM Customers_CTE
WHERE rowNum >= 0 and rowNum < 10
Data sample
CUSTOMERS
cust_id, cust_firstname, cust_lastname
--------------------------------------
1 , john , doe
2 , jane , mary
CUSTOM DATA
data_created, data_cust_id, data_key , data_value
------------------------------------------------------
2018-04-06 , 1 , 'Sign Date' , '2018-03-17'
2018-04-06 , 1 , 'Agreed' , 'Yes'
2019-03-12 , 1 , 'Renew Date' , '2019-01-25'
2020-04-11 , 2 , 'Sign Date' , '2020-03-28'
2020-04-11 , 2 , 'Agreed' , 'Yes'
2020-06-07 , 1 , 'Sign Date' , '2020-05-13'
2020-10-21 , 2 , 'Sign Date' , '2020-09-15'
RESULT
FirstName , LastName , DateSigned
-------------------------------------
jane , mary , 2020-09-15
john , doe , 2020-05-13
I'm struggling to see what's going on with your CTE or what you are trying to do, it's just wriddled with syntax errors or issues.
If this helps, to get your desired output from your sample data, you just need the following - if I've understood your sample data you just want the maximum date value for each customer Id for a particular "key" value:
select c.cust_firstname, c.cust_lastname, d.DateSigned
from customers c
outer apply (
select Max(Try_Convert(date,data_value)) DateSigned
from customdata cd
where cd.data_cust_id=c.cust_id and cd.data_key='Sign date'
)d

SQL : How to select the most recent value by country

I have a table with 3 columns : day, country, value. There are many values by country with different dates. For example :
DAY COUNTRY VALUE
04-SEP-19 BELGIUM 2124
15-MAR-19 BELGIUM 2135
21-MAY-19 SPAIN 1825
18-JUL-19 SPAIN 1724
26-MAR-19 ITALY 4141
I want to select the most recent value by country. For example :
DAY COUNTRY VALUE
04-SEP-19 BELGIUM 2124
18-JUL-19 SPAIN 1724
26-MAR-19 ITALY 4141
What is the sql query I can use?
Thank you for your help.
You can use the row_number() window function (if your DBMS supports it)).
SELECT x.day,
x.country,
x.value
FROM (SELECT t.day,
t.country,
t.value,
row_number() OVER (PARTITION BY t.country
ORDER BY t.day DESC) rn
FROM elbat t) x
WHERE x.rn = 1;
Another way of doing this is using a window function (SQL Server, MySQL8 etc)
e.g.
ROW_NUMBER() OVER ( PARTITION BY COUNTRY ORDER BY CONVERT(DATE, [Day]) DESC )
Then just filter to where this function returns 1
full example:
WITH TestData
AS ( SELECT '04-SEP-19' AS [Day], 'BELGIUM' AS [COUNTRY], 2124 AS [VALUE]
UNION
SELECT '15-MAR-19' AS [Day], 'BELGIUM' AS [COUNTRY], 2135 AS [VALUE]
UNION
SELECT '21-MAY-19' AS [Day], 'SPAIN' AS [COUNTRY], 1825 AS [VALUE]
UNION
SELECT '18-JUL-19' AS [Day], 'SPAIN' AS [COUNTRY], 1724 AS [VALUE]
UNION
SELECT '26-MAR-19' AS [Day], 'ITALY' AS [COUNTRY], 4141 AS [VALUE] ),
TestDataRanked
AS ( SELECT *,
ROW_NUMBER() OVER ( PARTITION BY COUNTRY ORDER BY CONVERT(DATE, [Day]) DESC ) AS SelectionRank
FROM TestData )
SELECT [Day],
COUNTRY,
[VALUE]
FROM TestDataRanked
WHERE SelectionRank = 1;
I understand the problem as you want the most recent value for all countries, as the country can repeat in the table(?):
select distinct t1.DAY, t1.COUNTRY, t1.VALUE
FROM day_test t1
inner join day_test t2 on t1.day in
(select max(day) from day_test t3 where t1.country = t3.country )
and t1.country = t2.country
I tested it and it works.
Let's suppose that the type of day column is date.
In the subquery, you can find the tuple of (country, max date) and to add the value, you can join as mentioned in the comments or use IN
SELECT DISTINCT day, country, value
FROM yourTable
WHERE (country, day)
in (
SELECT country, MAX(day) as day
FROM yourTable
GROUP BY country, value
)
You can use the following query:
Just replace the TABLE_NAME with the name of your table.
SELECT
COUNTRY,
VALUE,
MAX(DATE) AS "MostRecent"
FROM TABLE_NAME
GROUP BY COUNTRY;

Identify "Start" and "End" date of a trip in SQL?

I want to get the start and end date of the trip from a table as shown below -
I have tried Min(Date) and then Max(Date) Group by Line_Number,Country
Also
LEAD function
Preferred Outcome
Gaps and Islands...
Try this:
with tab (line_number, date, country) as (values
(123456, date('2019-05-20'), 'London')
, (123456, date('2019-05-21'), 'London')
, (123456, date('2019-05-22'), 'London')
, (123456, date('2019-06-27'), 'London')
, (123456, date('2019-06-28'), 'London')
, (123456, date('2019-06-29'), 'London')
, (123456, date('2019-06-30'), 'London')
, (123456, date('2019-07-01'), 'London')
)
, a as (
select t.*
-- OLAP function sums the "flags" computed below,
-- so each group with consecutive dates has its distinct group number
, sum
(
-- The expression below
-- returns 1 if date of the current row is 1 day after the date of the previous row
-- in the corresponding group (line_number, country) ordered by date
-- and 0 otherwise
case when date = lag(date) over (partition by line_number, country order by date) + 1 day then 0 else 1 end
) over (partition by line_number, country order by date) grp_num
from tab t
)
select
line_number
, min(date) as start_date
, max(date) as end_date
, country
--, grp_num
from a
group by line_number, country, grp_num;

Query column twice with union all

I am trying to do a union all to produce data for reporting, below is what I have so far, it shows all the data I want but I cannot get the data in the same rows, it produces the two rows at a minimum with null in the corresponding column. I am hoping that there is a way so that I can get the data in the same row?
select account, campaign, sale, date
from
(
SELECT CHACCOUNTNO as account, CONTSUPREF as campaign,null as sale, ONDATE as date
FROM dbo.MKTDW
WHERE (RESULTCODE = 'D01') and CONTACT IN ('Campaign ID')
group by CHACCOUNTNO, CONTSUPREF, ONDATE
UNION ALL
SELECT CHACCOUNTNO as account, null as campaign, CONTSUPREF as sale, ONDATE as date
FROM dbo.MKTDW
WHERE (RESULTCODE = 'D01') and CONTACT IN ('Order')
group by CHACCOUNTNO, CONTSUPREF, ONDATE
)account
group by account,campaign,sale,date
order by account
Current Result:
account campaign sale date
A2043056003(2IJUMI M NULL N177618 2014-07-21 00:00:00.000
A2043056003(2IJUMI M LT08704 NULL 2014-07-21 00:00:00.000
Expected result:
A2043056003(2IJUMI M) LT08704 N177618 2014-07-21 00:00:00.000
The answer will be straightforward. If you want something shown in horizontal way, use JOINs instead of SET operators. The code is listed below, tested, works perfect in SSMS. :)
--create table structure
create table dbo.MKTDW
( CHACCOUNTNO varchar(100),
CONTSUPREF varchar(10),
RESULTCODE varchar(10),
CONTACT varchar(50),
ONDATE datetime)
go
--insert sample data
insert dbo.MKTDW
select 'A2043056003(2IJUMI M)', 'N177618', 'D01', 'Order', '2014-07-21 00:00:00.000'
union all
select 'A2043056003(2IJUMI M)', 'LT08704', 'D01', 'Campaign ID', '2014-07-21 00:00:00.000'
union all
select 'B2043056003(2IJUMI M)', 'M000000', 'D01', 'Order', '2014-07-21 00:00:00.000'
union all
select 'B2043056003(2IJUMI M)', 'X111111', 'D01', 'Campaign ID', '2014-07-21 00:00:00.000'
--below is the solution
select a.CHACCOUNTNO as account,
a.CONTSUPREF as campaign,
b.CONTSUPREF as sale,
a.ondate as date
from dbo.MKTDW as a
join dbo.MKTDW as b
on a.CHACCOUNTNO = b.CHACCOUNTNO
where a.CONTACT = 'campaign id'
and b.CONTACT = 'order'
and a.RESULTCODE = 'D01'
and b.RESULTCODE = 'D01'
RESULT:
There is no need of union all and specifying null ..
SELECT CHACCOUNTNO as account, CONTSUPREF as campaign,CONTSUPREF as sale, ONDATE as date
FROM dbo.MKTDW
WHERE (RESULTCODE = 'D01') and CONTACT IN ('Campaign ID','order')
group by CHACCOUNTNO, CONTSUPREF, ONDATE
When any of the above group by columns have null,Null will be treated as seperate group

How do I select the most frequent value for a specific month and display this value as well as the amount of times it occurs?

I am struggling with a TSQL query and I'm all out of googling, so naturally I figured I might as well ask on SO.
Please keep in mind that I just began trying to learn SQL a few weeks back and I'm not really sure what rules there are and how you can and can not write your queries / sub-queries.
This is what I have so far:
Edit: Updated with DDL that should help create an example, also commented out unnecessary "Client"-column.
CREATE TABLE NumberTable
(
Number varchar(20),
Date date
);
INSERT INTO NumberTable (Number, Date)
VALUES
('55512345', '2015-01-01'),
('55512345', '2015-01-01'),
('55512345', '2015-01-01'),
('55545678', '2015-01-01'),
('55512345', '2015-02-01'),
('55523456', '2015-02-01'),
('55523456', '2015-02-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55534567', '2015-03-01'),
('55545678', '2015-03-01'),
('55545678', '2015-04-01')
DECLARE
--#ClientNr AS int,
#FromDate AS date,
#ToDate AS date
--SET #ClientNr = 11111
SET #FromDate = '2015-01-01'
SET #ToDate = DATEADD(yy, 1, #FromDate)
SELECT
YEAR(Date) AS [Year],
MONTH(Date) AS [Month],
COUNT(Number) AS [Total Count]
FROM
NumberTable
WHERE
--Client = #ClientNr
Date BETWEEN #FromDate AND #ToDate
AND Number IS NOT NULL
AND Number NOT IN ('888', '144')
GROUP BY MONTH(Date), YEAR(Date)
ORDER BY [Year], [Month]
With this I am getting the Year, Month and Total Count.
I'm happy with only getting the top 1 most called number and count each month, but showing top 5 is preferable.
Heres an example of how I would like the table to look in the end (having the months formatted as JAN, FEB etc instead of numbers is not really important, but would be a nice bonus):
╔══════╦═══════╦═════════════╦═══════════╦══════════╦═══════════╦══════════╗
║ Year ║ Month ║ Total Count ║ #1 Called ║ #1 Count ║ #2 Called ║ #2 Count ║
╠══════╬═══════╬═════════════╬═══════════╬══════════╬═══════════╬══════════╣
║ 2016 ║ JAN ║ 80431 ║ 555-12345 ║ 45442 ║ 555-94564 ║ 17866 ║
╚══════╩═══════╩═════════════╩═══════════╩══════════╩═══════════╩══════════╝
I was told this was "easily" done with a sub-query, but I'm not so sure...
Interesting one this, I believe you can do it with a CTE and PIVOT but this is off the top of my head... This may not work verbatim
WITH Rollup_CTE
AS
(
SELECT Client,MONTH(Date) as Month, YEAR(Date) as Year, Number, Count(0) as Calls, ROW_NUMBER() OVER (PARTITION BY Client,MONTH(Date) as SqNo, YEAR(Date), Number ORDER BY COUNT(0) DESC)
from NumberTable
WHERE Number IS NOT NULL AND Number NOT IN ('888', '144')
GROUP BY Client,MONTH(Date), YEAR(Date), Number
)
SELECT * FROM Rollup_CTE Where SqNo <=5
You may then be able to pivot the data as you wish using PIVOT
artm's query corrected (PARTITION) and the last step (pivoting) simplified.
with data AS
(select '2016-01-01' as called, '111' as number
union all select '2016-01-01', '111'
union all select '2016-01-01', '111'
union all select '2016-01-01', '222'
union all select '2016-01-01', '222'
union all select '2016-01-05', '111'
union all select '2016-01-05', '222'
union all select '2016-01-05', '222')
, ordered AS (
select called
, number
, count(*) cnt
, ROW_NUMBER() OVER (PARTITION BY called ORDER BY COUNT(*) DESC) rnk
from data
group by called, number)
select called, total = sum(cnt)
, n1= max(case rnk when 1 then number end)
, cnt1=max(case rnk when 1 then cnt end)
, n2= max(case rnk when 2 then number end)
, cnt2=max(case rnk when 2 then cnt end)
from ordered
group by called
EDIT Using setup provided by OP
WITH ordered AS(
-- compute order
SELECT
[Year] = YEAR(Date)
, [Month] = MONTH(Date)
, number
, COUNT(*) cnt
, ROW_NUMBER() OVER (PARTITION BY YEAR(Date), MONTH(Date) ORDER BY COUNT(*) DESC) rnk
FROM NumberTable
WHERE Date BETWEEN #FromDate AND #ToDate
AND Number IS NOT NULL
AND Number NOT IN ('888', '144')
GROUP BY YEAR(Date), MONTH(Date), number
)
-- pivot by order
SELECT [Year], [Month]
, total = sum(cnt)
, n1 = MAX(case rnk when 1 then number end)
, cnt1 = MAX(case rnk when 1 then cnt end)
, n2 = MAX(case rnk when 2 then number end)
, cnt2 = MAX(case rnk when 2 then cnt end)
-- n3, cnt3, ....
FROM ordered
GROUP BY [Year], [Month];
This query help you:
IF OBJECT_ID('tempdb..#Test','U') IS NOT NULL DROP TABLE #Test;
CREATE TABLE #Test(Number INT NOT NULL)
INSERT INTO #Test(Number)
VALUES(1),(2),(3),(1)
SELECT TOP 1 WITH TIES
Number
FROM (
SELECT DISTINCT
Number
, COUNT(*) OVER(PARTITION BY Number) AS cnt
FROM #Test) AS T
ORDER BY cnt DESC
I have used TOP 1 WITH TIES for case when max count exists for several values.
Try this, doesn't have to be CTE but I used it to populate data, you can extend it to include 3rd, 4th etc.
;with data AS
(select '2016-01-01' as called, '111' as number
union all select '2016-01-01', '111'
union all select '2016-01-01', '111'
union all select '2016-01-01', '222'
union all select '2016-01-01', '222')
, ordered AS (
select called
, number
, count(*) cnt
, ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) rnk
from data
group by called, number)
SELECT distinct *
FROM (SELECT DATENAME(month, called) mnth FROM ordered) AS mnth,
(SELECT number MostCalledNumber FROM ordered WHERE rnk = 1) AS MostCalledNumber,
(SELECT cnt MostCalledTimes FROM ordered WHERE rnk = 1) AS MostCalledTimes,
(SELECT number SecondMostCalledNumber FROM ordered WHERE rnk = 2) AS SecondMostCalledNumber,
(SELECT cnt SecondMostCalledTimes FROM ordered WHERE rnk = 2) AS SecondMostCalledTimes