INSERT INTO T_PROJECTGROUPSDATA (uploadID, fieldA,fieldB,......fieldN )
SELECT t.ID,p.fieldA,mg.fieldB,......mgc.fieldN
FROM T_Projects mp
INNER JOIN T_UPLOADS t mp.ID = t.project_savix_ID
INNER JOIN SG_Dynamic_Forms..v_projects p ON p.LegacyProjectId = mp.ID
INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_Project = p.ProjectID
INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_GroupID = mg.Grp_ID
INNER JOIN Savix_Service_Group..Group_Data mgd ON mgd.Gd_CycleID = mgc.Gc_ID
LEFT JOIN Savix_Service_Trainers..Trainers me ON me.Tr_ID = mgc.Gc_MonitoredBy
LEFT JOIN Savix_Service_Dictionaries..Dictionary mgt ON mgt.Dny_ID = me.Tr_Type
WHERE mp.SyncMode = 1
AND t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'
AND mg.Grp_IsDeleted = 0 AND mgd.Gd_IsDeleted != 1
UPDATE t SET
TOTALEXPENDITURES = CASE WHEN DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate) != 0
THEN p.Cost_Until_Today*DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate)/dbo.fn_RateAtDate(p.PROJECTCURRENCY_ID,dbo.fn_GetEndOfPeriod(t.Period_ID))
ELSE 0
END,
TotalExpendituresNative = CASE WHEN DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate) != 0
THEN p.Cost_Until_Today*DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate)
ELSE 0
END
FROM T_UPLOADS t
JOIN T_Projects mp ON mp.ID = t.project_savix_ID AND mp.SyncMode = 1
WHERE t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'
Aby wyjaśnić, jak to wymyśliłem, poniżej znajduje się zmodyfikowana wersja kodu. Nie mogę zagwarantować, że dostałem wszystko, musiałem poczynić pewne założenia. Brakowało przykładowych danych i brakowało wszystkich tabel savix. To najlepsza odpowiedź, jaką mogę znaleźć, biorąc pod uwagę informacje i ograniczając swój czas do mniej niż 4 zainwestowanych godzin. Mógłbym zrobić znacznie więcej, ale musiałbyś dać mi coś więcej niż tylko sławę internetową.
--ALTER PROCEDURE [dbo].[proc_Upload] WITH RECOMPILE
--as
set NoCount on
DECLARE
@StartTime datetime,
@EndTime datetime,
@DataID uniqueidentifier,
@CollectionDate datetime,
@Status int,
@PeriodID int,
@EndDate datetime,
@GroupID uniqueidentifier,
@ProjectID INT,
@FAID uniqueidentifier,
@UploadID int ,
@Createdate datetime,
@MINIDprojects INT,
@MAXIDprojects INT,
@MINIDdatasets INT,
@MAXIDdatasets INT,
@MINIDperiods INT,
@MAXIDperiods INT
Trudno jest pracować i testować kursory, więc zastąpiłem je (projekty, projekty1, zbiory danych i okres) tabelami tymczasowymi zawierającymi tożsamość i przepuściłem je.
IF OBJECT_ID('tempdb..#projects') IS NOT NULL DROP TABLE #projects
IF OBJECT_ID('tempdb..#projects1') IS NOT NULL DROP TABLE #projects1
IF OBJECT_ID('tempdb..#datasets') IS NOT NULL DROP TABLE #datasets
IF OBJECT_ID('tempdb..#period') IS NOT NULL DROP TABLE #period
CREATE TABLE #projects
(
[ProjectID] [INT],
[Title] [varchar](255) ,
[currency] [int] ,
[Cost_Until_Today] [float] ,
[StartDate] [datetime] ,
[EndDate] [datetime] ,
[MisID] [uniqueidentifier] ,
[SystemStatus] [int] ,
[FacilitatingAgency] [uniqueidentifier] ,
[SyncMode] [int]
)
CREATE TABLE #projects1
(
ID INT IDENTITY(1,1),
[ProjectID] [INT],
[FacilitatingAgency] [uniqueidentifier]
)
CREATE TABLE #datasets
(
ID INT IDENTITY(1,1),
Gd_ID [uniqueidentifier],
Grp_ID [uniqueidentifier],
Gd_CollectionDate DATETIME,
Gd_IsDeleted BIT,
Gd_CreateDate DATETIME
)
CREATE TABLE #period
(
ID INT IDENTITY(1,1),
IDPeriod INT,
EndDate DATETIME
)
INSERT #projects ( [ProjectID], [Title], {currency], [Cost_Until_Today], [StartDate], [EndDate], [MisID], [SystemStatus], [FacilitatingAgency], [SyncMode] )
SELECT ID, PROJECTNAME, PROJECTCURRENCY_ID, Cost_Until_Today, PROJECTESTABLISHEDDATE, EndDate, MisID, 4, FacilitatingAgency, SyncMode
FROM [dbo].[T_PROJECTS] /*thsi is the source table where every projectIDs need to be processed*/
Projects zawiera wszystkie identyfikatory ( projectID ) z T_Projects, jedyne pola, które są używane z tej tabeli to ID, projectcurrency, projectestablisheddate ( startdate ), enddate i syncmode ( musi być =1, zobaczymy to później ). Możemy zignorować stan systemu, ponieważ jest to stała.
Zestawy danych tabeli są usuwane i ładowane tutaj. Dbamy tylko o gd_id z tabeli Savix_Service_Group..Group_Data, ale nadal musimy dołączyć do innych tabel na wypadek, gdyby filtrowały wartości, których nie chcemy. Ta logika jest ponownie używana poniżej podczas wstawiania rekordów.
IF exists ( select * from dbo.sysobjects where id = object_id(N'[dbo].datasets]') and objectproperty(id, N'IsTable') = 1 ) DROP Table [dbo].datasets
SELECT mgd.Gd_ID, mg.Grp_ID, mgd.Gd_CollectionDate, mgd.Gd_IsDeleted, mgd.Gd_CreateDate, mg.Grp_Project, mg.Grp_IsDeleted , mg.Grp_Legacy_ID, p.LegacyProjectId
INTO datasets
FROM Savix_Service_Group..Group_Data mgd
INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_ID = mgc.Gc_GroupID
inner join SG_Dynamic_Forms..v_projects p ON p.ProjectID = mg.Grp_Project
--DECLARE projects1 CURSOR LOCAL FOR Select distinct ProjectID, isnull([FacilitatingAgency], '00000000-0000-0000-0000-000000000000') from @projects P WHERE P.SystemStatus = 4 AND P.SyncMode = 1
--/*First cursor - fetch the cursor from ProjectaTable*/
--OPEN projects1
--FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
--WHILE @@FETCH_STATUS = 0
Tutaj wstawiamy do projektów1 odrębną wartość identyfikatora projektu. Bez większej ilości danych zakładam, że identyfikator jest odrębny w tabeli T_projects i dlatego ten krok jest nieco niepotrzebny poza filtrowaniem przez SyncMode =1. Pamiętaj, że SystemStatus jest stałą i wypełniliśmy ją wartością „4”, to kryteria mogą zostać usunięte bez efektu.
INSERT INTO #projects1 ( ProjectID, FacilitatingAgency )
SELECT DISTINCT ProjectID, isnull([FacilitatingAgency], '00000000-0000-0000-0000-000000000000')
FROM #projects p
WHERE SystemStatus = 4 AND SyncMode = 1
SELECT @MINIDprojects = MIN(ID), @MAXIDprojects = MAX(ID) FROM #projects1
WHILE @MINIDprojects < @MAXIDprojects + 1
BEGIN
-- BEGIN TRY
-- BEGIN TRAN
SELECT @ProjectID = ProjectID, @FAID = FacilitatingAgency FROM #projects1 WHERE ID = @MINIDprojects
--DELETE FROM T_PROJECTGROUPSDATA WHERE T_PROJECTGROUPSDATA.UPLOAD_ID IN (SELECT ID FROM T_UPLOADS WHERE project_savix_ID = @ProjectID AND UPLOADFILENAME = 'Automatic upload from web MIS')
--DECLARE datasets CURSOR LOCAL FAST_FORWARD FOR SELECT Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate
-- FROM datasets
-- WHERE LegacyProjectId = @ProjectID AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1
-- /*Second cursor - this will get the 'collectionDate'field from datasetsTable for every project fetched in above cursor and also get @dataID which is used to insert value in to other table-T_PROJECTGROUPSDATA*/
--OPEN datasets
--FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
--WHILE @@FETCH_STATUS = 0
Tutaj wstawiamy filtrowanie #datasets według T_Project.ID AND Savix_Service_Group..Groups.Grp_IsDeleted =0 AND Savix_Service_Group..Group_Data.Gd_IsDeleted !=1
INSERT INTO #datasets ( Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate )
SELECT Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate FROM datasets WHERE LegacyProjectId = @ProjectID AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1
SELECT @MINIDdatasets = MIN(ID), @MAXIDdatasets = MAX(ID) FROM #datasets
WHILE @MINIDdatasets < @MAXIDdatasets + 1
BEGIN
SELECT @DataID = Gd_ID, @GroupID = Grp_ID, @CollectionDate = Gd_CollectionDate, @Status = Gd_IsDeleted, @Createdate = Gd_CreateDate FROM #datasets WHERE ID = @MINIDdatasets
--DECLARE period CURSOR LOCAL FAST_FORWARD FOR SELECT ID, dbo.fn_GetEndOfPeriod(ID) FROM T_PERIODS
--/* dbo.fn_GetEndOfPeriod(ID) - this function will give the end of the date of that specifc quarter for any given date*/
-- WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0
-- ORDER BY [YEAR],[Quarter]
-- /*Third Cursor - this will process the records from another table called period with above fetched @collectionDate*/
--OPEN period
--FETCH NEXT FROM period INTO @PeriodID, @EndDate
--WHILE @@FETCH_STATUS = 0
To chyba najgorsze użycie kursora tutaj. Wczytujemy wszystkie okresy i przechodzimy przez nie. W końcu interesują nas tylko okresy, które znajdują się w tabeli T_UPLOADS.
INSERT INTO #period ( IDPeriod, EndDate ) SELECT ID, dbo.fn_GetEndOfPeriod(ID) FROM T_PERIODS WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0
SELECT @MINIDperiods = MIN(ID), @MAXIDperiods = MAX(ID) FROM #period
WHILE @MINIDperiods < @MAXIDperiods + 1
BEGIN
--IF EXISTS (SELECT * FROM Savix_Service_Group..Group_Data mgd
-- INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
-- WHERE mgc.Gc_GroupID = @GroupID
-- AND DATEDIFF(dd,mgd.Gd_CollectionDate,@EndDate) >= 0
-- AND (mgd.Gd_CollectionDate > @CollectionDate )
-- AND mgd.Gd_IsDeleted != 1)
--BEGIN
-- BREAK
--END
Tutaj pomijamy wszelkie okresy, w których UPLOADFILENAME !='Automatyczne przesyłanie z sieci MIS'
--IF EXISTS (SELECT ID FROM T_UPLOADS u WHERE u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3 AND UPLOADFILENAME != 'Automatic upload from web MIS')
--BEGIN
-- FETCH NEXT FROM period INTO @PeriodID, @EndDate
-- CONTINUE
--END
Mając to na uwadze, obchodzi nas tylko, gdzie status =3 i UPLOADFILENAME =„Automatyczne przesyłanie z sieci MIS”
SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3 AND UPLOADFILENAME = 'Automatic upload from web MIS')
/*If T_uploads doesn't have appropirate period ID from cursor fetch then create a new entry in T_uploads with current projectID*/
IF @UploadID IS NOT NULL
BEGIN
--declare @Project_ID_Legacy int = ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID)
Dlaczego używają drugiej tabeli ( T_UPLOADFIRSTSTEP ) do generowania rekordu, w którym używają tylko identyfikatora, a następnie usuwają rekord przy użyciu tego identyfikatora podczas tworzenia rekordu T_Uploads, jest poza moim zrozumieniem i wydaje mi się, że kodowanie jest okropne.
-- INSERT INTO T_UPLOADSFIRSTSTEP
-- (PROJECT_ID
-- --,UPLOADDATE
-- --,UPLOADFILENAME
-- --,UPLOADUSER_ID
-- --,CURRENTSTEP
-- ,[STATUS]
-- ,Project_ID_MIS)
-- SELECT ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID),
-- --GETDATE(),
-- --'Automatic upload from web MIS',
-- --2,
-- --2,
-- 0,
-- @ProjectID
Wstawianie do T_UPLOADS zawsze zakończy się niepowodzeniem, ponieważ istnieją pola, które nie mogą mieć wartości null, których nie ma na liście wstawiania i nie mają przypisanej wartości domyślnej.
-- INSERT INTO T_UPLOADS ( ID, periodID, projectID,UPLOADDATE,UPLOADFILENAME,UPLOADUSER_ID )
-- SELECT uf.ID,
-- @PeriodID,
-- ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID),
-- GETDATE(),
-- 'Automatic upload from web MIS',
-- 2
-- FROM T_UPLOADSFIRSTSTEP uf
-- INNER JOIN #projects mp ON uf.Project_ID_MIS = mp.ProjectID
-- WHERE uf.Project_ID_MIS = @ProjectID AND uf.[STATUS] = 0
-- AND NOT EXISTS (SELECT * FROM T_UPLOADS u WHERE u.PROJECT_ID = uf.PROJECT_ID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3)
-- DELETE FROM T_UPLOADSFIRSTSTEP WHERE STATUS = 0 AND PROJECT_ID = ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID)
-- --SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3)
-- END
--ELSE
Oto prawdziwa logika, która jest wykonywana wewnątrz tych 3 kursorów. Rozumiejąc, że nie interesują nas okresy, logika przechodzi przez nie wszystkie. Dbamy tylko o to, że musimy filtrować określone kryteria ( status =3 i UPLOADFILENAME ='Automatyczne przesyłanie z web MIS' ) i że mają one pasującą tabelę T_Project.ID z kryteriami T_Projects.SyncMode =1
UPDATE t SET
TOTALEXPENDITURES = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0
THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.StartDate,mp.EndDate)/dbo.fn_RateAtDate(mp.Currency,dbo.fn_GetEndOfPeriod(t.Period_ID))
ELSE 0
END,
TotalExpendituresNative = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0
THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.StartDate,mp.EndDate)
ELSE 0
END
FROM T_UPLOADS t
JOIN #projects mp ON mp.ProjectID = t.project_savix_ID
WHERE 1=1 -- t.ID = @UploadID
--AND t.project_savix_ID = @ProjectID AND t.PERIOD_ID = @PeriodID
AND t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'
Wstawka do T_PROJECTGROUPDATA powiela logikę używaną do tworzenia zmiennej @dataid pochodzącej z #datasets. Który pochodzi z tabeli zestawów danych, którą porzuciliśmy i utworzyliśmy powyżej.
INSERT INTO T_PROJECTGROUPSDATA (uploadID, fieldA,fieldB,......fieldN )
SELECT @UploadID,p.fieldA,mg.fieldB,......mgc.fieldN
FROM #projects mp
inner join SG_Dynamic_Forms..v_projects p ON p.LegacyProjectId = mp.projectID
inner join Savix_Service_Group..Groups mg ON mg.Grp_Project = p.ProjectID
INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_GroupID = mg.Grp_ID
INNER JOIN Savix_Service_Group..Group_Data mgd ON mgd.Gd_CycleID = mgc.Gc_ID
LEFT JOIN Savix_Service_Trainers..Trainers me ON me.Tr_ID = mgc.Gc_MonitoredBy
LEFT JOIN Savix_Service_Dictionaries..Dictionary mgt ON mgt.Dny_ID = me.Tr_Type
--left join v1_Report_UDF_Data_UploadToSavix udf on udf.DataID = mgd.Gd_ID
WHERE mgd.Gd_ID = @DataID
--FETCH NEXT FROM period INTO @PeriodID, @EndDate
SET @MINIDperiods = @MINIDperiods + 1
END
--CLOSE period
--DEALLOCATE period
--FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
SET @MINIDdatasets = @MINIDdatasets + 1
END
--CLOSE datasets
--DEALLOCATE datasets
--COMMIT
--END TRY
--BEGIN CATCH
--SELECT ERROR_NUMBER(), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID
--IF CURSOR_STATUS('global' , 'period') >= 0
--BEGIN
-- CLOSE period
-- DEALLOCATE uploadID
--END
--IF CURSOR_STATUS('global' , 'datasets') >= 0
--BEGIN
-- CLOSE datasets
-- DEALLOCATE datasets
--END
--IF @@TRANCOUNT > 0
-- ROLLBACK
--INSERT INTO error_catch_UploadtoSavix
--SELECT cast(ERROR_NUMBER() as nvarchar), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID, getdate()
--END CATCH
SET @MINIDprojects = @MINIDprojects + 1
--FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
END
--CLOSE projects1
--DEALLOCATE projects1
--SELECT 1 as success
Masz to. Kondensacja prawie 300 linii i 3 kursory do 30 linii i brak kursorów.