Hi I have a table as below,
Id StartDt EndDt
----- -------- -------
123 01-Apr-2016 15-Apr-2016
456 02-Apr-2016 03-Apr-2016
I want to write a generic sql Insert statement that will work in all DB platforms.
So that I will insert a data similar to below into a new table from the above one
New Table value after Insert:
Id Date SeqNo
123 01-Apr-2016 1
123 02-Apr-2016 2
123 03-Apr-2016 3
123 04-Apr-2016 4
123 05-Apr-2016 5
123 06-Apr-2016 6
123 07-Apr-2016 7
123 08-Apr-2016 8
..
..
..
..
123 15-Apr-2016 15,
AssignmentID Date SeqNo
123 1-Apr 1
123 2-Apr 2
123 3-Apr 3
123 4-Apr 4
123 5-Apr 5
.
.
.
.
.
123 15-Apr 15
So fifteen rows for the first row i.e (End Date - Start Date) and similarly two rows for the second row from the main table(End Date - Start Date).
This will be best done in an UDTF. The UDTF will have the input 2 values and export the date range. This implementation will be similar to Explode.
You'd create an intervals table, containing all integers from 0 to 9999. You can do this programmatically:
CREATE TABLE intervals (period int);
INSERT INTO intervals (period) VALUES (0),(1);
DECLARE #rowCnt int;
SELECT #rowCnt = 2;
WHILE (SELECT Count(*) FROM intervals) < 10000 BEGIN
INSERT INTO intervals (period) SELECT period + #rowCnt FROM intervals WHERE period + #rowCnt < 10000;
SELECT #rowCnt = #rowCnt * 2
END;
The first time the loop runs, it inserts 2 rows with the values 2 & 3, then 4 rows etc, until the table is full.
Then you insert by cross joining this table with your table containing dates, and insert (start date + intervals (days)) where start date + interval <= finish date.
Related
I need to extract and migrate values from one table to another. the source table contains sumarized values for a specific effectivity date. If a value is changed, a new line is written if something is changed on the component values with the data valid starting at this effective date.
source_id
entity_id
effective_date
component_1
component_2
component_3
int(ai)
int
date
int
int
int
1
159
2020-01-01
100
0
90
2
159
2020-05-01
140
50
90
3
159
2020-08-01
0
30
90
5
159
2020-12-01
0
30
50
i need now migrate this data to a new table like this. the goal is that selecting data for a given month the result is the valid data for this month is given.
id
source_id
entity_id
startdate
enddate
component_type
value
int(ai)
int
int
date
date
int
int
each row represents a value for a component valid for a period of month.
I now run the insert update for each effective month by setting it as a parameter.
I insert value changes as new rows to the table an prevent duplicates by using a unique key (entity_id,effective_date,component_type)
SET #effective_date = '2020-01-01';
INSERT INTO component_final
select NULL,
source_id,
entity_id,
effective_date,
NULL,
1,
component_1
FROM component_source
WHERE effective_date = #effective_date
AND component_1>0;
after migrating the first row it should be that result
id
source_id
entity_id
startdate
enddate
component_type
value
1
1
159
2020-01-01
NULL
1
100
2
1
159
2020-01-01
NULL
3
90
SET #effective_date = '2020-05-01';
INSERT INTO component_final
select NULL,
source_id,
entity_id,
effective_date,
NULL,
1,
component_1
FROM component_source
WHERE effective_date = #effective_date
AND component_1>0;
after migrating the second row it should be that result
id
source_id
entity_id
startdate
enddate
component_type
value
1
1
159
2020-01-01
2020-04-30
1
100
2
1
159
2020-01-01
NULL
3
90
3
2
159
2020-05-01
NULL
1
140
4
2
159
2020-05-01
NULL
2
50
so if there is a value change in the future an end date has to be set.
I'm not able to do the second step, updating the data, if the component is changed in the future.
Maybe it is possible to have it as triggers after insert new row with same entity and component - but I was not able to make it work.
Some ideas? I want to handle this only inside of the MySQL.
You do not need the column enddate in the table component_final, because it's value depends on other values in the same table:
SELECT
id,
source_id,
entity_id,
startdate,
( SELECT DATE_ADD(MIN(cf2.startdate),INTERVAL -1 DAY)
FROM component_final cf2
WHERE cf2.startdate > cf1.startdate
AND cf2.source_id = cf1.source_id
AND cf2.entity_id = cf1.entity_id
) as enddate,
component_type,
value
FROM component_final cf1;
I understand that the core issue is how to find the source_ids where a component changes (0 means a removal, so we don't want these entries in the result) and how to assign the respective end dates at the same time. For the sake of illustration I simplify your example a bit:
There is only one component_type (I take into account that there might then be consecutive entries with unchanged value)
there is only one entity_id, so we can ignore it
It should be easy to extend this simpler version to your real-world problem.
To this is an example input:
source_id
effective_date
value
1
2020-01-01
100
2
2020-01-03
100
3
2020-01-05
80
4
2020-01-10
0
5
2020-01-12
30
I would expect the following output to be generated:
source_id
start_date
end_date
value
1
2020-01-01
2020-01-04
100
3
2020-01-05
2020-01-09
80
5
2020-01-12
NULL
30
You can achieve this with one query by joing each row with the previous one to check if the value has changed (find the start dates of periods) and the first row that is in the future and has a different value (find the start of the next period). If there is no previous row, it is considered a start as well. If there is no later update of the value, we have no end_date.
SELECT
main.source_id,
main.effective_date as start_date,
DATE_SUB(next_start.effective_date, INTERVAL 1 DAY) as end_date,
main.value
FROM source main
LEFT JOIN source prev ON prev.effective_date = (
SELECT MAX(effective_date)
FROM source
WHERE effective_date < main.effective_date
)
LEFT JOIN source next_start ON next_start.effective_date = (
SELECT MIN(effective_date)
FROM source
WHERE effective_date > main.effective_date AND value <> main.value
)
WHERE
ISNULL(prev.source_id) OR prev.value <> main.value
AND main.value <> 0
ORDER BY main.source_id
As I said: This will have to be adapted to your problem, e.g. by adding proper join conditions for the entity_id.
#Luuk pointed out that you don't need the end date because it can be derived from the data. This would be the case if you had entries for the start of "0 periods" as well, i.e. if there is no value set. If you don't have entries for these, you can't derive the end from the start of the respectively next period since there might be a gap in between.
This question already has answers here:
Get a list of dates between two dates
(23 answers)
Closed 4 years ago.
I want to display all the dates between two dates for a particular record
And this is the table :
ID Start_Date End_Date
-------------------------
1 2013-01-14 2013-01-18
2 2013-02-01 2013-02-04
and now i want to get all the dates between from date and to date.
Expected output
ID Date
-------------
1 2013-01-14
1 2013-01-15
1 2013-01-16
1 2013-01-17
1 2013-01-18
2 2013-02-01
2 2013-02-02
2 2013-02-03
2 2013-02-04
any help will be appreciated
EDIT:: this works on sql server (my bad didn't see the tag). Will probably do for mysql as well as it supports the same date functions
try something like this replacing my hard coded values with your table values. This brings back all days between 2 dates including the start date. If you wish to keep the max date too, you'll have to modify my code
create table #datediff
(
id int,
dates date
)
declare #min date, #max date,#df int
set #min = '2018-01-30'
set #max = '2018-02-5'
set #df = DATEDIFF(DAY,#min,#max)
while #df <> 0
begin
insert into #datediff
Values ( 1, DATEADD(D,-1,#max))
set #df = #df -1
set #max = DATEADD(D,-1,#max)
end
select * from #datediff
drop table #datediff
I've got two tables:
Table Transmission
------------------------------
Id GroupID Amount Timestamp
1 1 5 2015-05-20 00:00:00
2 1 4 2015-05-19 00:00:00
3 1 10 2015-04-20 00:00:00
4 1 7 2015-04-19 00:00:00
5 1 9 2015-03-20 00:00:00
6 1 2 2015-03-18 00:00:00
Table Group
---------
Id DateCreated BillStart BillStop
1 2015-03-15 2015-05-15 2015-06-14
BillStart and BillStop have a trigger on them that runs daily, if the current Date is greater than BillStop, both BillStart/BillStop increase by a month (so these are effectively the current range we are looking at).
I already have a SQL view that can sum up a range of the transmission entries where the timestamp is between BillStart and BillStop, what I am looking to do is have another view that would effectively hold the sums of transmissions of past months. i.e. if a BillStart/BillStop goes from 2015-05-15 to 2015-06-14, then the view would know to group past transmissions from the ranges of 2015-4-15 to 20-15-14, 2015-3-15 to 2015-4-14, and so on, up until the last range containing the groups creation date.
Ideally the view would look like
-------------------------------------
GroupId Sum BillStart BillStop
1 9 2015-05-15 2015-06-14
1 17 2015-04-15 2015-05-14
1 11 2015-03-15 2015-04-14
Is there a better way to do this?
Another option I was considering was a table for GroupBillRanges that would entail:
GroupBillRange
--------------
Id GroupId BillStart BillStop
1 1 2015-03-15 2015-04-14
2 1 2015-04-15 2015-05-14
3 1 2015-05-15 2015-06-14
And this would be added when each bill range gets updated by the month. With this I would just be able to match all Transmission.GroupId to GroupDateRange.GroupId
Yes, you would need to create the "GroupBillRange" table. Otherwise you lose any record of what the Bill Start/Stop dates were on the old groups. (Sure, you could assume that they are always from the 15th to the 14th. But the one time that they are not, you will have problems.) Once you do that, a query like the following should give you what you're looking for, I believe. (Oh, and you have this question tagged as both SQL Server and MySQL. So I created this script on SQL Server 2008 r2.)
SET NOCOUNT ON;
DECLARE #Transmission TABLE (
ID int,
GroupID int,
Amount int,
TransmissionDate SmallDateTime );
INSERT #Transmission VALUES (1, 1, 5,'2015/05/20');
INSERT #Transmission VALUES (2, 1, 4,'2015/05/19');
INSERT #Transmission VALUES (3, 1,10,'2015/04/20');
INSERT #Transmission VALUES (4, 1, 7,'2015/04/19');
INSERT #Transmission VALUES (5, 1, 9,'2015/03/20');
INSERT #Transmission VALUES (6, 1, 2,'2015/03/18');
DECLARE #TableGroupRange TABLE (
ID int,
GroupID int,
BillStart SmallDateTime,
BillStop SmallDateTime );
INSERT #TableGroupRange VALUES (1, 1, '2015/03/15', '2015/04/14' )
INSERT #TableGroupRange VALUES (2, 1, '2015/04/15', '2015/05/14' )
INSERT #TableGroupRange VALUES (3, 1, '2015/05/15', '2015/06/14' )
SET NOCOUNT OFF;
SELECT TG.GroupID, SUM(T.Amount) as SumAmount, TG.BillStart, TG.BillStop
FROM #TableGroupRange TG
LEFT JOIN #Transmission T ON T.GroupID = TG.GroupID AND T.TransmissionDate BETWEEN TG.BillStart AND TG.BillStop
GROUP BY TG.GroupID, TG.BillStart, TG.BillStop
ORDER BY TG.GroupID, TG.BillStart, TG.BillStop
SQL conundrum here.
I want to populate an empty table based on the data in an existing table using phpMyAdmin.
More specifically, I want to use the data in mark to create the data in attempt. The columns in mark are student_number, test_number, attempt_number, question_number and the answer. It's a multiple-choice test analysis tool.
mark (existing)
snum tnum anum qnum answer
----------------------------------------
1 1 1 1 A
1 1 1 2 C
1 1 1 3 D
1 1 2 1 B
1 1 2 2 A
1 1 2 3 C
attempt (to be created)
snum tnum anum period
--------------------------------
1 1 1 2013-1
1 1 2 2013-2
I can get the distinct snum, tnum, anum combinations as follows:
SELECT DISTINCT snum, tnum, anum FROM `mark`
How can I use the results from this call to populate the requisite insert call?
INSERT INTO `attempt` (snum,tnum,anum,period) VALUES (:s,:t,:a,"2013-1")
Ideally, I'd like to auto-complete the period value based on "2013-" plus the anum. I suspect this is not possible, so I'll just select all the anum=1 values, and hardcode the period value (and then repeat for each anum).
Thanks.
Use the "INSERT INTO SELECT" syntax:
INSERT INTO 'attempt'(snum,tnum,anum,period) SELECT DISTINCT snum,tnum,anum,CONCAT('2013-',anum) as period FROM mark;
INSERT INTO `attempt`
(snum,
tnum,
anum,
period)
select DISTINCT
snum,
tnum,
anum,
concat('2013-',anum) as period
from mark;
Good day
I've been struggling trying to make a query... maybe it is impossible to do it in a simple query... maybe not... that's why after several days I come to your help.
I have 7 joined tables, but for simplicity, let's say they are only 2
TblDeffects
------
Date IdOperation Deffects
2013-12-12 1 1
2013-12-12 2 1
2013-12-11 1 1
2013-12-11 3 1
2013-11-10 1 1
2013-11-09 5 1
TblOperations
------
Id Description
1 Operation 1
2 Operation 2
3 Operation 3
4 Operation 4
5 Operation 5
Now, supposing that Deffects lists is longer I want to join both tables, separating them by week (starting on friday and ending on thursday)
I can do that... I think...
But I also want to get ALL operations from the Operations table for each day...
so I could get something like
Output
------
StartDate IdOperation Deffects Description
2013-12-06 1 2 Operation 1
2013-12-06 2 1 Operation 2
2013-12-06 3 1 Operation 3
2013-12-06 4 0 Operation 4 <- No deffects for this
2013-12-06 5 0 Operation 5 <- No deffects for this
2013-11-08 1 1 Operation 1
2013-11-08 2 0 Operation 2 <- No deffects for this
2013-11-08 3 0 Operation 3 <- No deffects for this
2013-11-08 4 0 Operation 4 <- No deffects for this
2013-11-08 5 1 Operation 5
And if possible to get the missing weeks with a 0 in the deffects column...
(The empty row is just to separate the months and make it a little easier to read here)
Right now what I get are both joined tables and at the end the operation that wasn't included (in this case Operation #4 with a NULL date)
Is it possible to create a query like that??
Thank you
If you do not want to depend on the Deffects table being populated with data, you need a temporary table containing the dates you want to check. The only way to get this, is a loop unfortunately.
DECLARE #dates TABLE(Date DATETIME NOT NULL UNIQUE)
DECLARE #firstDate DATETIME = '2013-11-08T00:00:00Z'
DECLARE #weeks INT = 7
DECLARE #currentWeek INT = 0
WHILE (#currentWeek < #weeks) BEGIN
INSERT #dates VALUES (DATEADD(DAY, #currentWeek * 7, #firstDate))
SET #currentWeek = #currentWeek + 1
END
SELECT Dates.Date, Operations.Id IdOperation, (SELECT COUNT(*) FROM Defects WHERE Defects.date_inspected >= Dates.Date AND Defects.date_inspected < DATEADD(DAY, 7, Dates.Date) AND Defects.id_operation = Operations.Id) AS Deffects, Operations.Description
FROM #dates Dates
CROSS JOIN Operations
ORDER BY Dates.Date DESC
SqlFiddle for this: http://sqlfiddle.com/#!3/3142db/25
EDIT: Adjusted code to match the sqlFiddle provided.