2011-01-19 4 views
1

Я хочу знать, как я могу переписать этот SQL в один select, используя объединения. у меня есть длинный вытянутый путь, как показано ниже, чтобы в основном получить минимальную дату «начального этапа проекта» и максимальную дату для вехи «продление производства».Как я могу сделать эту процедуру лучше?

некоторый фон - это приложение для управления проектами, которое отслеживает вехи проектов по сравнению с базовым набором основных этапов релиза. Мне нужно иметь proc, который берет CSV-список идентификаторов проекта и выбирает min startDate для начального этапа проекта (StatusCode.cid = 37) и максимальной промежуточной стадии производства (StatusCode.cid = 77)

вот мой манекен SQL я работаю в настоящее время:

CREATE PROC rpt_rainbow 
@ProjectIDs NVARCHAR(1000) 
AS 

DECLARE @MinBRSProjectStartDate DATETIME 
DECLARE @MinBRSReleaseStartDate DATETIME 

DECLARE @MaxProdProjectEndDate DATETIME 
DECLARE @MaxProdReleaseEndDate DATETIME 

SELECT @MinBRSProjectStartDate = MIN (pm.startDate) 
FROM StatusCode sc  
INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID AND pm.ProjectID IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 
WHERE sc.cid =37 

SELECT @MinBRSReleaseStartDate = MIN(rel.startDate) 
FROM Project p 
INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID AND rel.milestonecid IN (37) 
WHERE ProjectId IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 

SELECT @MaxProdProjectEndDate = MAX (pm.endDate) 
FROM StatusCode sc  
INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID AND pm.ProjectID IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 
WHERE sc.cid =77 

SELECT @MaxProdReleaseEndDate = MAX(rel.endDate) 
FROM Project p 
INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID AND rel.milestonecid IN (77) 
WHERE ProjectId IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 


select isnull(@MinBRSProjectStartDate, @MinBRSReleaseStartDate) as MinBRS_StartDate, 
    isnull(@MaxProdProjectEndDate, @MaxProdReleaseEndDate) as MaxProd_EndDate 

вот моя функция разделения:

CREATE FUNCTION dbo.Split 
    ( @Delimiter varchar(5), 
     @List  varchar(8000) 
    ) 
    RETURNS @TableOfValues table 
     ( RowID smallint IDENTITY(1,1), 
     [Value] varchar(50) 
    ) 
AS 
    BEGIN 

     DECLARE @LenString int 

     WHILE len(@List) > 0 
     BEGIN 

      SELECT @LenString = 
       (CASE charindex(@Delimiter, @List) 
        WHEN 0 THEN len(@List) 
        ELSE (charindex(@Delimiter, @List) -1) 
       END 
       ) 

      INSERT INTO @TableOfValues 
       SELECT substring(@List, 1, @LenString) 

      SELECT @List = 
       (CASE (len(@List) - @LenString) 
        WHEN 0 THEN '' 
        ELSE right(@List, len(@List) - @LenString - 1) 
       END 
       ) 
     END 

     RETURN 

    END 

и здесь приведены определения для таблиц, участвующих:

CREATE TABLE [dbo].[ProjectMilestone](
    [ProjectMilestoneId] [int] NOT NULL, 
    [ProjectId] [int] NOT NULL, 
    [MilestoneCID] [int] NOT NULL, 
    [StartDate] [datetime] NOT NULL, 
    [EndDate] [datetime] NOT NULL, 
    [RAGStatusCID] [int] NOT NULL, 
    [CompletionStatusCID] [int] NOT NULL, 
    [StatusText] [nvarchar](max) NOT NULL, 
    [ReportingPriority] [int] NULL, 
    [Owner] [nvarchar](50) NOT NULL, 
    [Added] [datetime] NOT NULL, 
    [LastUpdate] [datetime] NOT NULL, 
    [UpdateBy] [nvarchar](50) NOT NULL, 
CONSTRAINT [PK_ProjectMilestone] PRIMARY KEY CLUSTERED 
(
    [ProjectMilestoneId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 
ALTER TABLE [dbo].[ProjectMilestone] WITH CHECK ADD CONSTRAINT [FK_ProjectMilestone_Project] FOREIGN KEY([ProjectId]) 
REFERENCES [dbo].[Project] ([ProjectId]) 
GO 
ALTER TABLE [dbo].[ProjectMilestone] CHECK CONSTRAINT [FK_ProjectMilestone_Project] 


----------------------------------------------------------------------------------------------- 

CREATE TABLE [dbo].[Project](
    [ProjectId] [int] NOT NULL, 
    [ProjectName] [nvarchar](255) NOT NULL, 
    [ProjectRegistration] [nvarchar](50) NOT NULL, 
    [CaseManagerBenId] [nvarchar](50) NOT NULL, 
    [ClarityId] [nvarchar](50) NOT NULL, 
    [ParentProjectId] [int] NULL, 
    [ReleaseId] [int] NOT NULL, 
    [CompletionStatusCID] [int] NOT NULL, 
    [ProjectTypeCID] [int] NOT NULL, 
    [Budget] [money] NOT NULL, 
    [BusinessObjective] [nvarchar](max) NOT NULL, 
    [Benefit] [nvarchar](max) NOT NULL, 
    [Added] [datetime] NOT NULL, 
    [LastUpdate] [datetime] NOT NULL, 
    [UpdateBy] [nvarchar](50) NOT NULL, 
    [StakeholderList] [nvarchar](1000) NULL, 
CONSTRAINT [PK_Project] PRIMARY KEY CLUSTERED 
(
    [ProjectId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 
ALTER TABLE [dbo].[Project] WITH CHECK ADD CONSTRAINT [FK_Project_Project] FOREIGN KEY([ParentProjectId]) 
REFERENCES [dbo].[Project] ([ProjectId]) 
GO 
ALTER TABLE [dbo].[Project] CHECK CONSTRAINT [FK_Project_Project] 
GO 
ALTER TABLE [dbo].[Project] WITH CHECK ADD CONSTRAINT [FK_Project_Release] FOREIGN KEY([ReleaseId]) 
REFERENCES [dbo].[Release] ([ReleaseId]) 
GO 
ALTER TABLE [dbo].[Project] CHECK CONSTRAINT [FK_Project_Release] 

-------------------------------------------------------------------------------------------- 

CREATE TABLE [dbo].[StatusCode](
    [CID] [int] NOT NULL, 
    [CodeName] [nvarchar](50) NOT NULL, 
    [Description] [nvarchar](max) NOT NULL, 
    [SCID] [int] NOT NULL, 
    [ReportingPriority] [int] NULL, 
CONSTRAINT [PK_StatusCode] PRIMARY KEY CLUSTERED 
(
    [CID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

-------------------------------------------------------------------------------------------- 

CREATE TABLE [dbo].[ReleaseSchedule](
    [ReleaseScheduleID] [int] NOT NULL, 
    [ReleaseID] [int] NOT NULL, 
    [MilestoneCID] [int] NOT NULL, 
    [StartDate] [datetime] NOT NULL, 
    [EndDate] [datetime] NOT NULL, 
    [Added] [datetime] NOT NULL, 
    [LastUpdate] [datetime] NOT NULL, 
    [UpdateBy] [nvarchar](50) NOT NULL, 
CONSTRAINT [PK_ReleaseSchedule] PRIMARY KEY CLUSTERED 
(
    [ReleaseScheduleID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 
ALTER TABLE [dbo].[ReleaseSchedule] WITH CHECK ADD CONSTRAINT [FK_ReleaseSchedule_Release] FOREIGN KEY([ReleaseID]) 
REFERENCES [dbo].[Release] ([ReleaseId]) 
GO 
ALTER TABLE [dbo].[ReleaseSchedule] CHECK CONSTRAINT [FK_ReleaseSchedule_Release] 
+0

Вы уверены, что это «работает» сейчас? вы используете SPLIT неправильным способом (delim/list) – RichardTheKiwi

+0

мое текущее решение работает, но кажется громоздким и, как горилла (я), написал его. – kacalapy

ответ

3

Есть две вещи отметить, первое, что вы могли бы уменьшить 4 запросов к 2

SELECT 
    @MinBRSProjectStartDate = MIN (CASE WHEN sc.cid=37 then pm.startDate END), 
    @MaxProdProjectEndDate = MAX (CASE WHEN sc.cid=77 then pm.endDate END) 
FROM StatusCode sc  
INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID 
    AND pm.ProjectID IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 
WHERE sc.cid in (37,77) 

SELECT 
    @MinBRSReleaseStartDate = MIN(CASE WHEN rel.milestonecid=37 then rel.startDate end), 
    @MaxProdReleaseEndDate = MAX(CASE WHEN rel.milestonecid=77 then rel.endDate end) 
FROM Project p 
INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID AND rel.milestonecid IN (37,77) 
WHERE ProjectId IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 

Там действительно нет способа соединения между этими двумя наборами, так что нет смысла пытаться. Но вы можете CROSS JOIN два результата однорядные, чтобы получить все 4 колонки в одном выберите:

SELECT ISNULL(A,C) as MinBRS_StartDate, ISNULL(B,D) AS MaxProd_EndDate 
FROM 
( 
SELECT 
    MIN (CASE WHEN sc.cid=37 then pm.startDate END) A, 
    MAX (CASE WHEN sc.cid=77 then pm.endDate END) B 
FROM StatusCode sc  
INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID 
    AND pm.ProjectID IN (SELECT value FROM fn_Split(@ProjectIDs, ',')) 
WHERE sc.cid in (37,77) 
) X, 
(
SELECT 
    MIN(CASE WHEN rel.milestonecid=37 then rel.startDate end) C, 
    MAX(CASE WHEN rel.milestonecid=77 then rel.endDate end) D 
FROM Project p 
INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID AND rel.milestonecid IN (37,77) 
WHERE ProjectId IN (SELECT value FROM fn_Split(@ProjectIDs, ','))) Y 

Но так как вы используете ISNULL через 2 пары, может быть лучше держать 4 целевых index- способные выбирать и просто подзапросить их. Поскольку вы используете значения SPLIT 4 раза, имеет смысл кэшировать его в таблице temp. ISNULL должен быть достаточно умным, чтобы не требовать оценки второго выбора, как только первое возвращает значение.

declare @ids table (id int) 
insert @ids SELECT distinct value FROM fn_Split(',', @ProjectIDs) V 

SELECT 
    ISNULL(
     (SELECT MIN (pm.startDate) 
      FROM StatusCode sc 
      INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID 
      INNER JOIN @ids I ON pm.ProjectID = I.ID 
      WHERE sc.cid =37), 
     (SELECT MIN(rel.startDate) 
      FROM Project p 
      INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID 
      INNER JOIN @ids I ON p.ProjectID = I.ID 
      WHERE rel.milestonecid IN (37))) AS MinBRS_StartDate, 
    ISNULL(
     (SELECT MAX (pm.endDate) 
      FROM StatusCode sc  
      INNER JOIN ProjectMilestone pm ON sc.CID = pm.MilestoneCID 
      INNER JOIN @ids I ON pm.ProjectID = I.ID 
      WHERE sc.cid =77), 
     (SELECT MAX(rel.endDate) 
      FROM Project p 
      INNER JOIN ReleaseSchedule rel ON rel.ReleaseID = p.ReleaseID 
      INNER JOIN @ids I ON p.ProjectID = I.ID 
      WHERE rel.milestonecid IN (77))) AS MaxProd_EndDate 
+0

+1 Очень хороший ответ, хорошо объясненный и подробный. – Lamak

Смежные вопросы