how can i make this proc better? - sql

i want to know how i can rewrite this SQL into a single select using joins. i have a long drawn out way as seen below to basically get the min date of a "project inception milestone" and max date for a "production go-live" milestone.
some background is the sql is for a project management application that tracks projects milestones against a release baseline set of milestones. I need to have a proc that takes a CSV list of projectIDs and have it select the min startDate for the project inception milestone (StatusCode.cid =37) and the max production go-live milestone(StatusCode.cid =77)
here is my dummy SQL i have working now:
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
here is my split function:
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
and here are the definitions for the tables involved:
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]

There are two things of note, the first is that you could reduce the 4 queries to 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, ','))
There is really no way to join between these two sets, so there is no point trying. But you could CROSS JOIN the two single-row results to get all 4 columns in a single select:
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
But since you are using ISNULL across the 2 pairs, it may be better to keep the 4 targeted index-able selects and to just subquery them. Since you are using the SPLIT values 4 times, it makes sense to cache it in a temp table. The ISNULL should be smart enough not to need to evaluate the 2nd select once the first returns a value.
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

Related

SQL Server Count items available

I am building a pretty simple inventory items database that will allow me to check out items between dates. I will need to return a single row that will tell me the available, reserved, and total inventory of this item. I cannot seem to get this correct.
BEGIN TRAN
DECLARE #startDate AS DATE
DECLARE #endDate AS DATE
DECLARE #partID AS INT
SET #startDate = '4/15/2015'
SET #endDate = '4/25/2015'
SET #partID = 248
SELECT COUNT(ii.[PartId] WHERE ii.[PartStatus] = 1 ) AS [Available],
COUNT(ii.[PartId] WHERE ii.[PartStatus] = 2 ) AS [Reserverd],
COUNT(ii.[PartId] WHERE ii.[PartStatus] <> 4 ) AS [TotalInventory],
FROM ShipListInventory.dbo.InventoryItems AS ii
LEFT JOIN ShipListInventory.dbo.InventoryItemCalendars AS iic
ON iic.[ItemId] = ii.[Id]
WHERE iic.[StartDate] NOT BETWEEN #startDate AND #endDate
AND iic.[InboundDate] NOT BETWEEN #startDate AND #endDate
AND ii.[PartId] = 248
COMMIT TRAN
EDIT: I am only returning rows from InventoryItemCalendarsI want to return items from InventoryItems
CREATE TABLE [dbo].[InventoryItems](
[Id] [INT] IDENTITY(1,1) NOT NULL,
[PartSatuts] [INT] NOT NULL,
[PartId] [INT] NOT NULL,
[Barcode] [NVARCHAR](MAX) NULL,
[PicturePath] [NVARCHAR](255) NULL,
[Notes] [NVARCHAR](1000) NULL,
CONSTRAINT [PK_dbo.InventoryItems] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[InventoryItems] WITH CHECK ADD CONSTRAINT [FK_dbo.InventoryItems_dbo.Parts_PartId] FOREIGN KEY([PartId])
REFERENCES [dbo].[Parts] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[InventoryItems] CHECK CONSTRAINT [FK_dbo.InventoryItems_dbo.Parts_PartId]
GO
CREATE TABLE [dbo].[InventoryItemCalendars](
[Id] [INT] IDENTITY(1,1) NOT NULL,
[StartDate] [DATETIME] NOT NULL,
[InboundDate] [DATETIME] NOT NULL,
[ProjectNumber] [INT] NOT NULL,
[ItemId] [INT] NOT NULL,
[Project_Id] [INT] NULL,
CONSTRAINT [PK_dbo.InventoryItemCalendars] PRIMARY KEY CLUSTERED
(
[Id] 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].[InventoryItemCalendars] WITH CHECK ADD CONSTRAINT [FK_dbo.InventoryItemCalendars_dbo.InventoryItems_ItemId] FOREIGN KEY([ItemId])
REFERENCES [dbo].[InventoryItems] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[InventoryItemCalendars] CHECK CONSTRAINT [FK_dbo.InventoryItemCalendars_dbo.InventoryItems_ItemId]
GO
ALTER TABLE [dbo].[InventoryItemCalendars] WITH CHECK ADD CONSTRAINT [FK_dbo.InventoryItemCalendars_dbo.Projects_Project_Id] FOREIGN KEY([Project_Id])
REFERENCES [dbo].[Projects] ([Id])
GO
ALTER TABLE [dbo].[InventoryItemCalendars] CHECK CONSTRAINT [FK_dbo.InventoryItemCalendars_dbo.Projects_Project_Id]
GO
INSERT INTO ShipListInventory.[dbo].[InventoryItems]([PartSatuts],[PartId]) VALUES(1,1);
INSERT INTO ShipListInventory.[dbo].[InventoryItems]([PartSatuts],[PartId]) VALUES(2,1);
INSERT INTO ShipListInventory.[dbo].[InventoryItems]([PartSatuts],[PartId]) VALUES(1,1);
INSERT INTO ShipListInventory.[dbo].[InventoryItems]([PartSatuts],[PartId]) VALUES(4,1);
INSERT INTO ShipListInventory.[dbo].[InventoryItemCalendars](StartDate,InboundDate,Project_Id, ItemId)VALUES('1/1/2015', '4/15/2015',1,1)
INSERT INTO ShipListInventory.[dbo].[InventoryItemCalendars](StartDate,InboundDate,Project_Id, ItemId)VALUES('1/1/2015', '4/15/2015',1,1)
INSERT INTO ShipListInventory.[dbo].[InventoryItemCalendars](StartDate,InboundDate,Project_Id, ItemId)VALUES('1/1/2015', '4/14/2015',1,2)
INSERT INTO ShipListInventory.[dbo].[InventoryItemCalendars](StartDate,InboundDate,Project_Id, ItemId)VALUES('1/1/2015', '4/15/2015',1,1)
this isnt returning data from correct table it is only returning data form InventoryItemCalendars which isnt what i want. i want it to only use this to check the dates. it should return the items from InventoryItems table
Here is a syntactically correct version:
SELECT SUM(CASE WHEN ii.[PartStatus] = 1 THEN 1 ELSE 0 END) AS [Available],
SUM(CASE WHEN ii.[PartStatus] = 2 THEN 1 ELSE 0 END) AS [Reserverd],
SUM(CASE WHEN ii.[PartStatus] <> 4 THEN 1 ELSE 0 END) AS [TotalInventory]
FROM ShipListInventory.dbo.InventoryItems ii LEFT JOIN
ShipListInventory.dbo.InventoryItemCalendars AS iic
ON iic.[ItemId] = ii.[Id]
WHERE iic.[StartDate] NOT BETWEEN #startDate AND #endDate AND
iic.[InboundDate] NOT BETWEEN #startDate AND #endDate AND
ii.[PartId] = 248;
There is no transaction when you do a select, so committing is not necessary.
SELECT COUNT(CASE WHEN ii.[PartStatus] = 1 THEN ii.[PartId] END) AS [Available],
COUNT(CASE WHEN ii.[PartStatus] = 2 THEN ii.[PartId] END) AS [Reserverd],
COUNT(CASE WHEN ii.[PartStatus] <> 4 THEN ii.[PartId] END) AS [TotalInventory]
With the help of Juan Carlos I have found the correct answer.
SELECT COUNT(CASE WHEN ii.[PartSatuts]=1 THEN ii.[PartId] END ) AS [Available],
COUNT(CASE WHEN ii.[PartSatuts]=2 THEN ii.[PartId] END ) AS [Reserverd],
COUNT(CASE WHEN ii.[PartSatuts]<> 4 THEN ii.[PartId] END ) AS [TotalInventory]
FROM ShipListInventory.dbo.InventoryItems AS ii
LEFT JOIN ShipListInventory.dbo.InventoryItemCalendars AS iic
ON iic.ItemId = ii.Id
WHERE ((iic.StartDate NOT BETWEEN #startDate AND #endDate
AND iic.InboundDate NOT BETWEEN #startDate AND #endDate)
OR iic.InboundDate IS NULL)
AND ii.PartId = 248

sql running total or Balance

I need running VoucherNo concatenation just like running balance or total.. Concatenate the previous VoucherNo to current VoucherNo row wise just like shown in picture
Query is:
select
v.VoucherDate,v.VoucherNo,v.VoucherType,v.Narration,SUM(v.Debit) Debit , SUM(v.Credit) Credit,dbo.GetBalance(v.CompanyProfileId,v.AccountCode,v.VoucherDate ,SUM(v.Debit), SUM(v.Credit)) Balance
from AcVoucher v
where v.VoucherDate Between '2016-03-24' and '2016-03-30' and v.CompanyProfileId = 2 and v.AccountCode = '05010001'
group by v.VoucherNo,v.VoucherDate,v.VoucherType,v.Narration,v.CompanyProfileId,v.AccountCode
Schema :
GO
/****** Object: Table [dbo].[AcVoucher] Script Date: 03/30/2016 3:47:02 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[AcVoucher](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[CompanyProfileId] [int] NOT NULL,
[AccountCode] [nvarchar](50) NOT NULL,
[VoucherNo] [bigint] NOT NULL,
[VoucherType] [nvarchar](5) NOT NULL,
[VoucherDate] [datetime] NOT NULL,
[Narration] [nvarchar](500) NULL,
[Debit] [float] NOT NULL,
[Credit] [float] NOT NULL,
[TaxPercentage] [float] NULL,
[DiscountPercentage] [float] NULL,
[CreatedBy] [int] NULL,
[CreatedDate] [datetime] NULL,
CONSTRAINT [PK_ACVoucher_1] PRIMARY KEY CLUSTERED
(
[Id] 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
You can do this using basically the same logic as for string concatenation in SQL Server. The only difference is the where clause.
One key issue is the ordering for the concatenation. This is not apparent in the question, so I added a minid to the query and this is used (in reverse order) for selecting the ids to bring together:
with v as (
select v.VoucherDate, v.VoucherNo, v.VoucherType, v.Narration,
SUM(v.Debit) as Debit , SUM(v.Credit) as Credit,
dbo.GetBalance(v.CompanyProfileId, v.AccountCode, v.VoucherDate,
SUM(v.Debit), SUM(v.Credit)
) as Balance,
min(id) as minid
from AcVoucher v
where v.VoucherDate Between '2016-03-24' and '2016-03-30' and
v.CompanyProfileId = 2 and v.AccountCode = '05010001'
group by v.VoucherNo, v.VoucherDate, v.VoucherType,v.Narration, v.CompanyProfileId, v.AccountCode
)
select v.*,
stuff((select ',' + cast(v2.VoucherNo as varchar(8000))
from v v2
where v2.minid >= v.minid
for xml path ('')
), 1, 1, '') as RunningConcat
from v;

Dynamic column create from child table

I have two table one for master and another child. Which is depicting below
Master table:
and scripts:
CREATE TABLE [dbo].[SET_HRShiftProfile](
[id] [smallint] NOT NULL,
[LocationID] [tinyint] NOT NULL,
[ShiftTypeID] [smallint] NOT NULL,
[ProfileName] [nvarchar](50) NULL,
[EmpTypeCode] [nvarchar](10) NOT NULL,
[IsActive] [bit] NOT NULL,
CONSTRAINT [PK_HR_ShiftProfile] PRIMARY KEY CLUSTERED
(
[id] 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
Child Table:
And script:
CREATE TABLE [dbo].[SET_HRShiftProfileDetail](
[id] [int] NOT NULL,
[LocationID] [tinyint] NOT NULL,
[ShiftprofileID] [smallint] NOT NULL,
[ShiftProfTypeCode] [nvarchar](10) NOT NULL,
[Start] [nvarchar](5) NULL,
[End] [nvarchar](5) NULL,
[ToleranceBefore] [nvarchar](5) NULL,
[ToleranceAfter] [nvarchar](5) NULL,
CONSTRAINT [PK_SET_HRShiftProfileDetail] PRIMARY KEY CLUSTERED
(
[id] 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
I need to final query result look like this, means all child table row will be column for master table
Any idea give me appreciate. Thanks.
Try this:
with pivoted as
(
select LocationID, ShiftprofileID, [IN], [OUT], [Break]
from (
select distinct LocationID, ShiftprofileID, [ShiftProfTypeCode]
from [dbo].[SET_HRShiftProfileDetail]
) as t
pivot (
count([ShiftProfTypeCode]) for [ShiftProfTypeCode] in ([IN], [OUT], [Break])
) as p
)
select p.*
, x.[IN], x.[OUT], x.[Break]
from [dbo].[SET_HRShiftProfile] as p
left join pivoted x on x.ShiftprofileID = p.id
and x.locationid = p.locationid
Results:
id LocationID ShiftTypeID ProfileName EmpTypeCode IsActive IN OUT Break
1003 1 1001 Day-Summar REG 1 1 1 1
1006 1 1005 Say-Winter REG 1 1 1 0
Since you said nothing about what data you like to get into cells, I have placed there the count of rows with such value (as column name) from the child table.
Hope it will help you to solve your problem
LATER EDIT
Solution without using PIVOT:
with pivoted as
(
select LocationID
, ShiftprofileID
, sum(case when [ShiftProfTypeCode] = 'OUT' then 1 else 0 end) [OUT]
, sum(case when [ShiftProfTypeCode] = 'IN' then 1 else 0 end) [IN]
, sum(case when [ShiftProfTypeCode] = 'Break' then 1 else 0 end) [Break]
from [dbo].[SET_HRShiftProfileDetail]
group by LocationID, ShiftprofileID
)
select p.*
, x.[IN], x.[OUT], x.[Break]
from [dbo].[SET_HRShiftProfile] as p
left join pivoted x on x.ShiftprofileID = p.id
and x.locationid = p.locationid
Results:
id LocationID ShiftTypeID ProfileName EmpTypeCode IsActive IN OUT Break
1003 1 1001 Day-Summar REG 1 1 1 1
1006 1 1005 Say-Winter REG 1 1 1 0

SQL Descending ordered LEFT JOIN subquery issue

I have the following query.
SELECT r1.*,
r2.vlag54,
r2.vlag55
FROM [rxmon].[dbo].[a] AS r1
LEFT JOIN [rxmon].[dbo].[b] AS r2
ON r2.artikelnummer = r1.drug_id
LEFT JOIN (SELECT *
FROM [rxmon].[dbo].[c]) AS r3
ON r3.pid = r1.patient_id
WHERE r3.obx_id = 20937
AND Cast(r3.obx_datetime AS DATE) = Cast(Getdate() - 1 AS DATE)
AND r1.patient_id = 7092425
AND obx_value < CASE
WHEN r2.vlag54 = 1 THEN 30
WHEN r2.vlag55 = 1 THEN 50
END
AND r2.vlag54 = CASE
WHEN r3.obx_value < 30 THEN 1
ELSE 0
END
AND r2.vlag55 = CASE
WHEN r3.obx_value BETWEEN 30 AND 50 THEN 1
ELSE 0
END
ORDER BY obx_datetime DESC;
The problem is that table C can contain multiple records based on de PID join. This generates the same records because of the multiple records on table C.
The table C needs to e joined as the latest record only so just 1 of C. That way the table A record will not be repeated.
I tried TOP 1 and order by but that can't be used in subquery.
-- TABLE A
CREATE TABLE [dbo].[A]
[EVS_MO_ID] [bigint] NOT NULL,
[DRUG_ID] [varchar](50) NOT NULL,
[ATC_CODE] [varchar](15) NULL,
[DRUG_NAME] [varchar](1024) NULL,
[PATIENT_ID] [varchar](50) NOT NULL,
[PATIENT_LOCATION] [varchar](10) NULL,
[MO_DATE] [datetime2](7) NOT NULL,
[MO_START_DATE] [datetime2](7) NOT NULL,
[MO_STOP_DATE] [datetime2](7) NULL,
[ROUTE] [varchar](50) NULL,
[MEDICATION_CONTAINER] [smallint] NULL,
[PRESCRIBING_DOCTOR_NAME] [varchar](50) NULL,
[PRESCRIBING_DOCTOR_SURNAME] [varchar](50) NULL,
[MO_ACTIVE] [bit] NOT NULL,
CONSTRAINT [PK_MedicationOrders] PRIMARY KEY CLUSTERED
(
[EVS_MO_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [dbo].[A]
VALUES
(5411409,'97941689', 'B01AB06','NADROPARINE 0.8ML','7092425','ANBC', '2015-12-15 20:58:06.2030000',
'2015-12-16 00:00:00.0000000', '', 'IV', 1, 'GEORGE','LAST', 1);
-- TABLE B
CREATE TABLE [dbo].[B](
[ID] [int] IDENTITY(1,1) NOT NULL,
[ARTIKELNUMMER] [varchar](50) NOT NULL,
[VLAG54] [bit] NULL,
[VLAG55] [bit] NULL CONSTRAINT [DF_Table_1_VLAG50] DEFAULT ((0)),
[VLAG100] [bit] NULL CONSTRAINT [DF_ArtikelVlaggen_VLAG100] DEFAULT ((0)),
CONSTRAINT [PK_B] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [dbo].[B]
([ARTIKELNUMMER]
,[VLAG54]
,[VLAG55]
,[VLAG100])
VALUES
('97941689', 1,0,1);
-- TABLE C
CREATE TABLE [dbo].[C](
[ID] [int] IDENTITY(1,1) NOT NULL,
[OBX_DATETIME] [datetime2](7) NOT NULL,
[PID] [int] NOT NULL,
[DEPARTMENT] [varchar](8) NOT NULL,
[OBX_ID] [int] NOT NULL,
[OBX_VALUE] [decimal](5, 2) NOT NULL,
[OBX_UNITS] [varchar](10) NULL,
[REF_RANGE] [varchar](40) NULL,
[FLAG] [varchar](2) NULL,
CONSTRAINT [PK_C] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [dbo].[C]
([OBX_DATETIME]
,[PID]
,[DEPARTMENT]
,[OBX_ID]
,[OBX_VALUE]
,[OBX_UNITS]
,[REF_RANGE]
,[FLAG])
VALUES
('2015-12-15 14:01:00.0000000',7092425, '8NAH', 20937, 27.00, 'mL/min', '> 60', 'L');
INSERT INTO [dbo].[C]
([OBX_DATETIME]
,[PID]
,[DEPARTMENT]
,[OBX_ID]
,[OBX_VALUE]
,[OBX_UNITS]
,[REF_RANGE]
,[FLAG])
VALUES
('2015-12-15 06:30:00.0000000',7092425, '6ZPA', 20937, 28.00, 'mL/min', '> 60', 'L');
This will order them by OBX_DATETIME and take only the first one:
...
LEFT JOIN (
SELECT pid, obx_id, obx_datetime, obx_value
, n = ROW_NUMBER() over(PARTITION BY pid ORDER BY obx_datetime desc)
FROM [rxmon].[dbo].[c]
) AS r3
ON r3.pid = r1.patient_id and r3.n = 1
...
If OBX_DATETIME are inserted incrementaly (newer date only), you can order by ID instead.
This SQL Fiddle with your query and sample data/tables returns 2 rows: http://sqlfiddle.com/#!3/df36c/2/0
This SQL Fiddle with the new subquery returns 1 row: http://sqlfiddle.com/#!3/df36c/1/0
You are using a LEFT JOIN on r3 but have also have r3 in your WHERE clause with equal operator:
WHERE r3.obx_id = 20937
AND Cast(r3.obx_datetime AS DATE) = Cast(Getdate() - 1 AS DATE)
It will remove NULL value from the left join on r3. Perhaps you should also move it to the sub query or use INNER JOIN.
You should also avoind using the DB name in your query unless this query is run from another DB on the same server. This will be fine:
SELECT ... FROM [dbo].[a] AS r1 ...
Using SELECT * is also a bad habit. You should list only the columns your code will use.
try this.... #Shift
SELECT r1.*,
r2.vlag54,
r2.vlag55
FROM [dbo].[a] AS r1
LEFT JOIN [dbo].[b] AS r2
ON r2.artikelnummer = r1.drug_id
LEFT JOIN (
SELECT
ROW_NUMBER() OVER (PARTITION BY pid ORDER BY id DESC) RN,
c.*
FROM C
) r3
ON r3.pid = r1.patient_id AND r3.RN = 1
WHERE r3.obx_id = 20937
AND Cast(r3.obx_datetime AS DATE) = Cast(Getdate() - 1 AS DATE)
AND r1.patient_id = 7092425
AND obx_value < CASE
WHEN r2.vlag54 = 1 THEN 30
WHEN r2.vlag55 = 1 THEN 50
END
AND r2.vlag54 = CASE
WHEN r3.obx_value < 30 THEN 1
ELSE 0
END
AND r2.vlag55 = CASE
WHEN r3.obx_value BETWEEN 30 AND 50 THEN 1
ELSE 0
END
ORDER BY obx_datetime DESC;

Query multilayer table in sql

I am trying to retrieve the UserInfo with the UserId=10004 with all his friends and all the posts of his friends and all the likes on those posts. And for that I am using a query :
select *,
(select * ,
(select * ,
(select * from PostLikes where PostId=UserPosts.PostId) as likes
from UserPosts where UserId=FriendsRelation.PersonId1 or UserId=FriendsRelation.PersonId2) as posts
from FriendsRelation where PersonId1=UserId or PersonId2=UserId) as friends
from UserInfo
where UserId=10004
but it is returning with an error
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS
How can I solve it?
Here are the tables that I am using:
CREATE TABLE [dbo].[UserInfo]
(
[UserId] [bigint] IDENTITY(1,1) NOT NULL,
[Username] [nvarchar](max) NULL,
[Email] [nvarchar](max) NOT NULL,
[UserPassword] [nvarchar](max) NOT NULL,
[Name] [nvarchar](max) NULL,
[Gender] [nchar](10) NOT NULL,
[ContactNo] [bigint] NOT NULL,
[DateOfBirth] [date] NOT NULL,
[RelationshipStatus] [nchar](10) NULL,
[InterestedIn] [nchar](10) NULL,
[Address] [nvarchar](max) NULL,
[Country] [nchar](10) NOT NULL,
[FavouriteQuote] [nvarchar](max) NULL,
[DisplayPhoto] [nvarchar](max) NULL,
[Guid] [nvarchar](max) NOT NULL,
[Status] [tinyint] NOT NULL CONSTRAINT [DF_UserInfo_Status] DEFAULT ((1)),
[CreatedDate] [datetime] NOT NULL,
[LastLogIn] [datetime] NULL,
CONSTRAINT [PK_UserInfo]
PRIMARY KEY CLUSTERED ([UserId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
UserPosts table:
CREATE TABLE [dbo].[UserPosts]
(
[PostId] [bigint] IDENTITY(1,1) NOT NULL,
[UserId] [bigint] NOT NULL,
[PostText] [nvarchar](max) NULL,
[PostPicture] [nvarchar](max) NULL,
[Time] [time](7) NOT NULL,
[Date] [date] NOT NULL,
[LikeCount] [int] NULL CONSTRAINT [DF_UserPosts_LikeCount] DEFAULT ((0)),
CONSTRAINT [PK_UserPosts]
PRIMARY KEY CLUSTERED ([PostId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[UserPosts] WITH CHECK
ADD CONSTRAINT [FK_UserPosts_UserInfo]
FOREIGN KEY([UserId]) REFERENCES [dbo].[UserInfo] ([UserId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[UserPosts] CHECK CONSTRAINT [FK_UserPosts_UserInfo]
GO
and PostLikes table:
CREATE TABLE [dbo].[PostLikes]
(
[LikeId] [bigint] IDENTITY(1,1) NOT NULL,
[PostId] [bigint] NOT NULL,
[UserId] [bigint] NOT NULL,
CONSTRAINT [PK_PostLike]
PRIMARY KEY CLUSTERED ([PostId] ASC, [UserId] 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].[PostLikes] WITH CHECK
ADD CONSTRAINT [FK_PostLikes_UserInfo]
FOREIGN KEY([UserId]) REFERENCES [dbo].[UserInfo] ([UserId])
GO
ALTER TABLE [dbo].[PostLikes] CHECK CONSTRAINT [FK_PostLikes_UserInfo]
GO
ALTER TABLE [dbo].[PostLikes] WITH CHECK
ADD CONSTRAINT [FK_PostLikes_UserPosts]
FOREIGN KEY([PostId]) REFERENCES [dbo].[UserPosts] ([PostId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[PostLikes] CHECK CONSTRAINT [FK_PostLikes_UserPosts]
GO
Similar to other people's answers, but use left joins for everything (in case the user has no friends), and some changes to join logic.
declare #userID int = 10004 --For easy swapping out if you want to query someone else
Select * from UserInfo a
left join FriendsRelation b
on b.PersonID1 = #userID or b.PersonID2 = #userID --Faster than joining and then filtering, if you're only looking for one person at a time
left join UserPosts c
on (c.UserID = b.PersonID1 or c.UserID = b.PersonID2)
and c.UserID <> #userID --returns only friends' posts, not user's posts, as specified in original question
left join postLikes d
on d.PostID = c.PostID
where a.UserID = #userID
You can use JOINS like this:
select *
from UserInfo
inner join FriendsRelation on UserInfo.UserId = FriendsRelation.PersonId1 or UserInfo.UserId = FriendsRelation.PersonId2
inner join UserPosts on FriendsRelation.PersonId1 = UserPosts.UserId
inner join PostLikes on UserPosts.PostId = PostLikes.PostId
where UserInfo.UserId=10004
As already mentioned, you need to use join instead of subqueries. Probably this query will be useful for you
select *
from UserInfo
inner join FriendsRelation
on UserInfo.UserId = FriendsRelation.PersonId1
inner join UserPosts
on FriendsRelation.PersonId1 = UserPosts.UserId
left outer join PostLikes
on UserPosts.PostId = PostLikes.PostId
where UserInfo.UserId=10004
union
select *
from UserInfo
inner join FriendsRelation
on UserInfo.UserId = FriendsRelation.PersonId2
inner join UserPosts
on FriendsRelation.PersonId2 = UserPosts.UserId
left outer join PostLikes
on UserPosts.PostId = PostLikes.PostId
where UserInfo.UserId=10004