Join a temporary table to query result in SQL - sql

I'm fairly new to SQL and looking for a simple/succinct way to achieve the following:
I have a query which has a nested select statement, and this nested select returns the data to a temporary table. I would for this temp table to be joined onto the initial return, however, I am just unsure about how I would achieve this.
SELECT Profile.profilename as Recipient,
Message.messagetext as MessageText,
message.datesent as DateSent
FROM Profile
INNER JOIN
Message ON Profile.profile_id = Message.profile_idtoo
WHERE Message.profile_idfrom = 3
(
SELECT profilename from Profile
Inner JOIN
Message on Profile.profile_id = Message.profile_idfrom
WHERE Message.profile_idfrom = 3
)
--Create tables and data.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Message](
[message_id] [int] IDENTITY(1,1) NOT NULL,
[messagetext] [nvarchar](max) NOT NULL,
[datesent] [date] NOT NULL,
[profile_idtoo] [int] NOT NULL,
[profile_idfrom] [int] NOT NULL,
CONSTRAINT [Message_pk] PRIMARY KEY CLUSTERED
(
[message_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
/****** Object: Table [dbo].[Profile] Script Date: 20/11/2018 13:14:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Profile](
[profile_id] [int] IDENTITY(1,1) NOT NULL,
[profilename] [nvarchar](255) NOT NULL,
[regdate] [date] NOT NULL,
[user_id] [int] NOT NULL,
CONSTRAINT [Profile_pk] PRIMARY KEY CLUSTERED
(
[profile_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
SET IDENTITY_INSERT [dbo].[Message] ON
INSERT [dbo].[Message] ([message_id], [messagetext], [datesent], [profile_idtoo], [profile_idfrom]) VALUES (1, N'This playlist is the best', CAST(N'2018-11-13' AS Date), 9, 8)
INSERT [dbo].[Message] ([message_id], [messagetext], [datesent], [profile_idtoo], [profile_idfrom]) VALUES (2, N'I got superscared by a few of those moviews tbh', CAST(N'2017-12-14' AS Date), 2, 3)
INSERT [dbo].[Message] ([message_id], [messagetext], [datesent], [profile_idtoo], [profile_idfrom]) VALUES (3, N'I think you should look atmy playlist called "the best songs of all time". ', CAST(N'2017-09-14' AS Date), 2, 5)
INSERT [dbo].[Message] ([message_id], [messagetext], [datesent], [profile_idtoo], [profile_idfrom]) VALUES (4, N'Best thing I ever done signin up to this site!', CAST(N'2018-11-13' AS Date), 6, 7)
SET IDENTITY_INSERT [dbo].[Message] OFF
SET IDENTITY_INSERT [dbo].[Profile] ON
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (1, N'TheSpazzCommander', CAST(N'2017-02-01' AS Date), 1)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (2, N'CaptainBuzzkill', CAST(N'2015-01-01' AS Date), 1)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (3, N'EVLM', CAST(N'2018-08-15' AS Date), 2)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (4, N'JBlunt', CAST(N'2017-07-15' AS Date), 3)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (5, N'JaneHeart', CAST(N'2015-05-01' AS Date), 4)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (6, N'JimmyHeart', CAST(N'2015-01-05' AS Date), 4)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (7, N'Khall', CAST(N'2014-01-01' AS Date), 6)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (8, N'thehunter', CAST(N'2017-01-01' AS Date), 7)
INSERT [dbo].[Profile] ([profile_id], [profilename], [regdate], [user_id]) VALUES (9, N'thehunterswife', CAST(N'2017-01-01' AS Date), 7)
SET IDENTITY_INSERT [dbo].[Profile] OFF
ALTER TABLE [dbo].[Message] WITH CHECK ADD CONSTRAINT [message_Profile_from] FOREIGN KEY([profile_idfrom])
REFERENCES [dbo].[Profile] ([profile_id])
GO
ALTER TABLE [dbo].[Message] CHECK CONSTRAINT [message_Profile_from]
GO
ALTER TABLE [dbo].[Message] WITH CHECK ADD CONSTRAINT [message_Profile_too] FOREIGN KEY([profile_idtoo])
REFERENCES [dbo].[Profile] ([profile_id])
GO
ALTER TABLE [dbo].[Message] CHECK CONSTRAINT [message_Profile_too]
GO
ALTER TABLE [dbo].[Profile] WITH CHECK ADD CONSTRAINT [Profile_User] FOREIGN KEY([user_id])
REFERENCES [dbo].[User] ([user_id])
GO
ALTER TABLE [dbo].[Profile] CHECK CONSTRAINT [Profile_User]
GO
Thanks for any help.

You can try below
DEMO
SELECT Profile.profile_id,Profile.profilename as Recipient, m1.profilename,
Message.messagetext as MessageText,
message.datesent as DateSent
FROM Profile
INNER JOIN
Message ON Profile.profile_id = Message.profile_idtoo
inner join (SELECT profile_id,profilename from Profile
Inner JOIN
Message on Profile.profile_id = Message.profile_idfrom
WHERE Message.profile_idfrom = 3) m1 on Message.profile_idfrom = m1.profile_id
WHERE Message.profile_idfrom = 3

Without knowing what else you want from the second table in your result set this may also work...keep in mind you have inner joins from the same table on two different fields, you may be excluding results or it may be the intended result :) More info would be helpful!
SELECT PROFILE.profilename AS Recipient,
MessageToo.messagetext AS MessageText,
MessageToo.datesent AS DateSent
FROM PROFILE
INNER JOIN Message MessageToo ON PROFILE.profile_id = Message.profile_idtoo
INNER JOIN Message MessageFrom ON PROFILE.profile_id = Message.profile_idfrom
WHERE Message.profile_idfrom = 3
SELECT PROFILE.profilename AS Recipient,
MessageToo.messagetext AS MessageText,
MessageToo.datesent AS DateSent
FROM PROFILE
LEFT JOIN Message MessageToo ON PROFILE.profile_id = Message.profile_idtoo
WHERE PROFILE.profile_idfrom = 3

Related

How to return "most populated","least populated" countries grouped by continent organized in a single table via SQL?

This is a variant of the SQLzoo tutorial.
'world' table contains fields
'population'(assigned to each country),
'name' (all countries) and
'continent' (assigned to each country).
Expected output is a table as shown below
Continent
Most_populous
Least_populous
Africa
Ghana
xyz
Asia
China
abc
I did try a complicated function as below, but was not able to get it to work due to "SQL error". Not sure why.
SELECT DISTINCT continent
, (SELECT x.name
FROM world x
WHERE x.population = (SELECT max(y.population)
FROM world y
WHERE x.continent = y.continent)) AS most_populous
, (SELECT z.name
FROM world z
WHERE z.population = (SELECT min(a.population)
FROM world a
WHERE a.continent=z.continent)) AS least_populous FROM world;
Is there an easier way to get the required output?
You can try this:
SELECT world.continent,
(SELECT x.name
FROM world x
WHERE x.population = (SELECT max(y.population)
FROM world y
WHERE x.continent = y.continent)
AND world.continent = x.continent
) AS most_populous,
(SELECT z.name
FROM world z
WHERE z.population = (SELECT min(a.population)
FROM world a
WHERE z.continent = a.continent)
AND world.continent = z.continent
) AS least_populous
FROM world
GROUP BY world.continent;
Thank you
Since I did not had the tables with me, I ended up creating one. It would be better if you could include table and data creation scripts in question. Second, this is going to be a fairly small table so I am not going to worry about performance. I would create a view based on this query and that will be good enough.
Table creation scripts:
USE [StackOverflow]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CountryPopulation]') AND type in (N'U'))
DROP TABLE [dbo].[CountryPopulation]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CountryPopulation](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[Continent] [nvarchar](max) NULL,
[Population] [int] NULL,
CONSTRAINT [PK_CountryPopulation] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [StackOverflow]
GO
SET IDENTITY_INSERT [dbo].[CountryPopulation] ON
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (1, N'C1', N'Asia', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (2, N'C2', N'Asia', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (3, N'C3', N'Asia', 300)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (4, N'C4', N'Europe', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (5, N'C5', N'Europe', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (6, N'C6', N'Europe', 300)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (7, N'C7', N'Africa', 100)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (8, N'C8', N'Africa', 200)
GO
INSERT [dbo].[CountryPopulation] ([ID], [Name], [Continent], [Population]) VALUES (9, N'C9', N'Africa', 200)
GO
SET IDENTITY_INSERT [dbo].[CountryPopulation] OFF
GO
Query to get result:
SELECT MinMax.Continent,
ForLeast.[name] AS LeastPopulous,
ForMost.[name] AS MostPopulous
FROM CountryPopulation ForMost JOIN
CountryPopulation ForLeast JOIN
( SELECT DISTINCT Continent,
MIN([population]) OVER(PARTITION BY continent) AS LeastPopulation,
MAX([population]) OVER(PARTITION BY continent) AS MaxPopulation
FROM CountryPopulation) MinMax
ON ForLeast.Continent = MinMax.Continent AND ForLeast.[Population] = MinMax.LeastPopulation
ON ForMost.Continent = MinMax.Continent AND ForMost.[Population] = MinMax.MaxPopulation
Note the results for Africa. There are 2 rows. It is possible for more than one country to have least or most population. You would want to think on how to handle that scenario.

Cartesian product between two tables when the second one doesn't have all records

I have 2 tables in SQL Server and I would like to get all possible combinations by their IDs even when the second one has no records, I would like to know if there is a better solution than mine.
My 2 tables are:
Companies (CompanyId, Tot1, Tot2) with 10 records
Motors (MotorId, CompanyId_FK, TotSales1, TotSales2) with 4 records
, here the script to create and populate them:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Companies](
[CompanyId] [int] IDENTITY(1,1) NOT NULL,
[Tot1] [int] NOT NULL,
[Tot2] [int] NOT NULL,
CONSTRAINT [PK_Companies] PRIMARY KEY CLUSTERED
(
[CompanyId] 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
/****** Object: Table [dbo].[Motors] Script Date: 10/15/2021 9:18:31 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Motors](
[MotorId] [int] IDENTITY(1,1) NOT NULL,
[CompanyId] [int] NOT NULL,
[TotSales1] [int] NOT NULL,
[TotSales2] [int] NOT NULL,
CONSTRAINT [PK_Motors] PRIMARY KEY CLUSTERED
(
[MotorId] 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
SET IDENTITY_INSERT [dbo].[Companies] ON
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (1, 300, 200)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (2, 400, 100)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (3, 500, 100)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (4, 600, 200)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (5, 700, 500)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (6, 800, 400)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (7, 900, 300)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (8, 50, 20)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (9, 80, 20)
GO
INSERT [dbo].[Companies] ([CompanyId], [Tot1], [Tot2]) VALUES (10, 40, 10)
GO
SET IDENTITY_INSERT [dbo].[Companies] OFF
GO
SET IDENTITY_INSERT [dbo].[Motors] ON
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (1, 4, 35, 23)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (2, 5, 140, 70)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (3, 7, 200, 24)
GO
INSERT [dbo].[Motors] ([MotorId], [CompanyId], [TotSales1], [TotSales2]) VALUES (4, 9, 2, 1)
GO
SET IDENTITY_INSERT [dbo].[Motors] OFF
GO
ALTER TABLE [dbo].[Motors] WITH CHECK ADD CONSTRAINT [FK_Motors_Companies] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[Companies] ([CompanyId])
GO
ALTER TABLE [dbo].[Motors] CHECK CONSTRAINT [FK_Motors_Companies]
GO
I need as result set a list of all possible combination containing a Motor and a Company even when there is no sales:
CompanyId
MotorId
Tot1
Tot2
TotSales1
TotSales2
4
1
600
200
35
23
4
2
600
200
NULL
NULL
4
3
600
200
NULL
NULL
4
4
600
200
NULL
NULL
I created a CTE by CROSS JOIN with only CompanyId and MotorId and then I joined the other 2 in this way:
;WITH myTable AS (
SELECT Companies.CompanyId, MotorId
FROM Companies CROSS JOIN Motors
GROUP BY Companies.CompanyId, MotorId)
SELECT myTable.*, Tot1, Tot2, TotSales1,TotSales2 FROM myTable
INNER JOIN Companies ON myTable.CompanyId = Companies.CompanyId
LEFT OUTER JOIN Motors ON myTable.CompanyId = Motors.CompanyId AND myTable.MotorId = Motors.MotorId
Is there a better solution? To avoid misunderstanding: I can't change the sequence of the fields to map the results with a C# class.
You were so close to the answer... just do not do the GROUP BY in your CTE and you will get the result that you want:
;WITH myTable AS (
SELECT Companies.CompanyId, MotorId
FROM Companies CROSS JOIN Motors
)
SELECT myTable.*, Tot1, Tot2, TotSales1,TotSales2 FROM myTable
INNER JOIN Companies ON myTable.CompanyId = Companies.CompanyId
LEFT OUTER JOIN Motors ON myTable.CompanyId = Motors.CompanyId AND myTable.MotorId = Motors.MotorId
I am not sure why you thought you needed the GROUP BY, but since you wanted all possible combinations there is no need for it.

Top three records for each branch

I need to write query for top three record(count and sum) for each branch of company and I have branch costumers and contracts tables
First of all, create a table like this:
CREATE TABLE [dbo].[Branches](
[Id] [int] NOT NULL,
[CompanyId] [int] NULL,
[Something] [int] NULL,
CONSTRAINT [PK_Table_5] 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
Then, insert some data into it:
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (1, 1, 10)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (2, 1, 15)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (3, 1, 20)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (4, 1, 25)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (5, 1, 22)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (6, 2, 50)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (7, 2, 32)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (8, 2, 30)
GO
INSERT [dbo].[Branches] ([Id], [CompanyId], [Something]) VALUES (9, 2, 10)
GO
In the end, your query will be like this:
SELECT result.Companyid, SUM(result.Something) [Sum], (SELECT COUNT(*) FROM Branches WHERE CompanyId = result.companyid) AS [Count]
FROM (
SELECT companyid, Something, Rank()
OVER (PARTITION BY CompanyId
ORDER BY id DESC ) AS Rank
FROM Branches
) result WHERE Rank <= 3
GROUP BY CompanyId
Schema:
First data:
Result:

Employee Month-Wise Service Length Using Sql

I am trying to create employee unit-wise service length report from their joining date and have used the following query to do so:
SELECT o.UnitName, p.DeptName, COUNT(m.EmpId) AS cnt,
(SELECT COUNT(m.EmpId) FROM EmpInf m
WHERE m.Desg IN ('Jr. Operator', 'Operator') AND m.Active = 'Active' AND m.DeptId = 2
AND DATEDIFF(MONTH, m.Joindate, GETDATE()) BETWEEN 0 AND 6) AS '0 - 6 Months'
FROM EmpInf m
INNER JOIN Department k ON k.DeptId = m.DeptId
INNER JOIN Section l ON l.secId = m.SecID
INNER JOIN UnitInf o ON o.UnitID = l.UnitName
INNER JOIN Department p ON p.DeptId = m.DeptId
WHERE Desg IN ('Jr. Operator', 'Operator') AND Active = 'Active' AND p.DeptName = 'Production'
GROUP BY o.UnitName, p.DeptName
Expected output as below: (As unit 1 and 4 have entry in the year 2017 means during 0 - 6 months of the year 2017 and there will be many others like 7 - 12, 13 - 24 etc)
Currently getting this:
I guess, having issue with the query and would be glad to know if there are any changes or alternates to do so.
Below is the script:
USE [sample]
GO
/****** Object: Table [dbo].[UnitInf] Script Date: 05/11/2017 21:19:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[UnitInf](
[UnitID] [int] IDENTITY(1,1) NOT NULL,
[UnitName] [nvarchar](100) NULL,
CONSTRAINT [PK_UnitInf] PRIMARY KEY CLUSTERED
(
[UnitID] 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
SET IDENTITY_INSERT [dbo].[UnitInf] ON
INSERT [dbo].[UnitInf] ([UnitID], [UnitName]) VALUES (1, N'Unit-01')
INSERT [dbo].[UnitInf] ([UnitID], [UnitName]) VALUES (2, N'Unit-02')
INSERT [dbo].[UnitInf] ([UnitID], [UnitName]) VALUES (3, N'Unit-03')
INSERT [dbo].[UnitInf] ([UnitID], [UnitName]) VALUES (4, N'Unit-04')
INSERT [dbo].[UnitInf] ([UnitID], [UnitName]) VALUES (5, N'Unit-05')
SET IDENTITY_INSERT [dbo].[UnitInf] OFF
/****** Object: Table [dbo].[Section] Script Date: 05/11/2017 21:19:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Section](
[secId] [int] IDENTITY(1,1) NOT NULL,
[SecName] [nvarchar](100) NULL,
[UnitName] [int] NULL,
CONSTRAINT [PK_Section] PRIMARY KEY CLUSTERED
(
[secId] 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
SET IDENTITY_INSERT [dbo].[Section] ON
INSERT [dbo].[Section] ([secId], [SecName], [UnitName]) VALUES (1, N'B-001', 1)
INSERT [dbo].[Section] ([secId], [SecName], [UnitName]) VALUES (2, N'C-001', 2)
INSERT [dbo].[Section] ([secId], [SecName], [UnitName]) VALUES (3, N'B-002', 1)
INSERT [dbo].[Section] ([secId], [SecName], [UnitName]) VALUES (4, N'D-004', 4)
SET IDENTITY_INSERT [dbo].[Section] OFF
/****** Object: Table [dbo].[EmpInf] Script Date: 05/11/2017 21:19:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmpInf](
[EmpId] [int] IDENTITY(1,1) NOT NULL,
[DeptId] [int] NULL,
[SecID] [int] NULL,
[EmpName] [nvarchar](100) NULL,
[GrossSal] [float] NULL,
[Desg] [nvarchar](100) NULL,
[SkillBonus] [float] NULL,
[Active] [nvarchar](10) NULL,
[JoinDate] [datetime] NULL,
CONSTRAINT [PK_EmpInf] PRIMARY KEY CLUSTERED
(
[EmpId] 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
SET IDENTITY_INSERT [dbo].[EmpInf] ON
INSERT [dbo].[EmpInf] ([EmpId], [DeptId], [SecID], [EmpName], [GrossSal], [Desg], [SkillBonus], [Active], [JoinDate]) VALUES (1, 2, 2, N'John', 10000, N'Operator', 2000, N'Active', CAST(0x0000A59F00000000 AS DateTime))
INSERT [dbo].[EmpInf] ([EmpId], [DeptId], [SecID], [EmpName], [GrossSal], [Desg], [SkillBonus], [Active], [JoinDate]) VALUES (2, 2, 2, N'Jack', 12000, N'Operator', 5000, N'Active', CAST(0x0000A5BC00000000 AS DateTime))
INSERT [dbo].[EmpInf] ([EmpId], [DeptId], [SecID], [EmpName], [GrossSal], [Desg], [SkillBonus], [Active], [JoinDate]) VALUES (3, 2, 4, N'Nick', 14000, N'Jr. Operator', 6000, N'Active', CAST(0x0000A75100000000 AS DateTime))
INSERT [dbo].[EmpInf] ([EmpId], [DeptId], [SecID], [EmpName], [GrossSal], [Desg], [SkillBonus], [Active], [JoinDate]) VALUES (4, 2, 4, N'Bruce', 15000, N'Operator', 7000, N'Active', CAST(0x0000A79000000000 AS DateTime))
INSERT [dbo].[EmpInf] ([EmpId], [DeptId], [SecID], [EmpName], [GrossSal], [Desg], [SkillBonus], [Active], [JoinDate]) VALUES (5, 2, 1, N'Willy', 16000, N'Jr. Operator', 8000, N'Active', CAST(0x0000A7B800000000 AS DateTime))
SET IDENTITY_INSERT [dbo].[EmpInf] OFF
/****** Object: Table [dbo].[Department] Script Date: 05/11/2017 21:19:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department](
[DeptId] [int] IDENTITY(1,1) NOT NULL,
[DeptName] [nvarchar](100) NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DeptId] 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
SET IDENTITY_INSERT [dbo].[Department] ON
INSERT [dbo].[Department] ([DeptId], [DeptName]) VALUES (1, N'Admin')
INSERT [dbo].[Department] ([DeptId], [DeptName]) VALUES (2, N'Production')
SET IDENTITY_INSERT [dbo].[Department] OFF
I think that simple conditional aggregation is the best approach:
SELECT o.UnitName, p.DeptName, COUNT(m.EmpId) AS cnt,
SUM(CASE WHEN DATEDIFF(MONTH, m.Joindate, GETDATE()) BETWEEN 0 AND 6
THEN 1 ELSE 0
END) AS [0 - 6 Months]
FROM EmpInf m INNER JOIN
Department k
ON k.DeptId = m.DeptId INNER JOIN
Section l
ON l.secId = m.SecID INNER JOIN
UnitInf o
ON o.UnitID = l.UnitName INNER JOIN
Department p
ON p.DeptId = m.DeptId
WHERE Desg IN ('Jr. Operator', 'Operator') AND Active = 'Active' AND
p.DeptName = 'Production'
GROUP BY o.UnitName, p.DeptName
I haven't run your code, but it looks like you're hardcoding too much in your subquery.
(SELECT COUNT(m.EmpId) FROM EmpInf m
WHERE m.Desg IN ('Jr. Operator', 'Operator') AND m.Active = 'Active' AND m.DeptId = 2
AND DATEDIFF(MONTH, m.Joindate, GETDATE()) BETWEEN 0 AND 6) AS '0 - 6 Months'
Problem 1: your alias for EmpInf in the subquery is "m" and your alias for EmpInf in your main query is "m". Make them different so you can link them.
Problem 2: Connect your variables in the subquery to values in the main query. So:
(SELECT COUNT(m.EmpId) FROM EmpInf subm
WHERE subm.Desg=m.Desg AND subm.Active = m.Active AND subm.DeptId = m.DeptId
AND DATEDIFF(MONTH, subm.Joindate, GETDATE()) BETWEEN 0 AND 6) AS '0 - 6 Months'

Sql Recursion via CTE over LINQ SelectMany

Firstly I'm fairly well versed in LINQ queries, but a complete novice at writing direct SQL queries.
I want to be able to do the following:
For any given ItemId, loop through it's child Items until the very base child items have been selected.
Each item belongs to a container, which has a base (or parent) container Id specified (or NULL if it is the base container). A container can only have one parent container, but it can have multiple child containers.
Currently I've been doing something like the following:
using (MyEntities db = new MyEntities())
{
var theItem = db.Find(itemId);
var theContainer = theItem.Container.BaseContainer;
var theBaseItems = theItem.BaseItems.Where(bi => bi.ContainerId == theContainer.ContainerId).ToList();
while (theContainer.BaseContainerId != null)
{
theContainer = theContainer.BaseContainer;
theBaseItems = theBaseItems.SelectMany(bi => bi.BaseItems.Where(i => i.ContainerId == theContainer.ContainerId)).ToList();
}
}
This runs fine and fairly speedy, however when the ContainerId is quite high up the chain, I've noticed the ridiculous number of queries to the database caused by the SelectMany. For example, if 1000 Items belonged across 100 Items in a parent container, and those 100 items belonged across 10 Items in that containers parent container and finally those 10 belong to 1 item at the top of the chain, the Select Many will run 10 + 100 queries, flattening the results each time to retrieve the base 1000 items - AFAIK to be expected.
I have therefore suspected (after much research) that a Sql CTE may be a better option, not only hitting the database a little more gently, but possibly faster too - is this a bad assumption?
I am however struggling to get to grips with the CTE syntax and am hoping someone out there can shed their wisdom on my problem and help me out.
Re-creating the scenario
USE [TestDatabase]
GO
/****** Object: Table [dbo].[Containers] Script Date: 20/05/2013 14:17:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Containers](
[ContainerId] [int] IDENTITY(1,1) NOT NULL,
[BaseContainerId] [int] NULL,
[Name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Containers] PRIMARY KEY CLUSTERED
(
[ContainerId] 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
/****** Object: Table [dbo].[ItemRelationships] Script Date: 20/05/2013 14:17:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ItemRelationships](
[ChildItemId] [int] NOT NULL,
[ParentItemId] [int] NOT NULL,
CONSTRAINT [PK_ItemRelationships] PRIMARY KEY CLUSTERED
(
[ChildItemId] ASC,
[ParentItemId] 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
/****** Object: Table [dbo].[Items] Script Date: 20/05/2013 14:17:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Items](
[ItemId] [int] IDENTITY(1,1) NOT NULL,
[ContainerId] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED
(
[ItemId] 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
SET IDENTITY_INSERT [dbo].[Containers] ON
INSERT [dbo].[Containers] ([ContainerId], [BaseContainerId], [Name]) VALUES (1, NULL, N'Level 1')
INSERT [dbo].[Containers] ([ContainerId], [BaseContainerId], [Name]) VALUES (2, 1, N'Level 2')
INSERT [dbo].[Containers] ([ContainerId], [BaseContainerId], [Name]) VALUES (3, 1, N'Level 2b')
INSERT [dbo].[Containers] ([ContainerId], [BaseContainerId], [Name]) VALUES (4, 2, N'Level 3')
INSERT [dbo].[Containers] ([ContainerId], [BaseContainerId], [Name]) VALUES (5, NULL, N'TypeB')
SET IDENTITY_INSERT [dbo].[Containers] OFF
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (1, 13)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (2, 13)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (3, 13)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (4, 14)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (5, 14)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (6, 14)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (7, 15)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (8, 15)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (9, 15)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (10, 16)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (11, 16)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (12, 16)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (13, 17)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (14, 17)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (15, 17)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (1007, 13)
INSERT [dbo].[ItemRelationships] ([ChildItemId], [ParentItemId]) VALUES (1008, 17)
SET IDENTITY_INSERT [dbo].[Items] ON
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (1, 1, N'A')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (2, 1, N'B')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (3, 1, N'C')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (4, 1, N'D')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (5, 1, N'E')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (6, 1, N'F')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (7, 1, N'G')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (8, 1, N'H')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (9, 1, N'I')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (10, 1, N'J')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (11, 1, N'K')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (12, 1, N'L')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (13, 2, N'A2')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (14, 2, N'A2')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (15, 2, N'C2')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (16, 3, N'D2B')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (17, 4, N'A3')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (1007, 5, N'TypeB1')
INSERT [dbo].[Items] ([ItemId], [ContainerId], [Name]) VALUES (1008, 5, N'TypeB2')
SET IDENTITY_INSERT [dbo].[Items] OFF
ALTER TABLE [dbo].[Containers] WITH CHECK ADD CONSTRAINT [FK_Containers_Containers] FOREIGN KEY([BaseContainerId])
REFERENCES [dbo].[Containers] ([ContainerId])
GO
ALTER TABLE [dbo].[Containers] CHECK CONSTRAINT [FK_Containers_Containers]
GO
ALTER TABLE [dbo].[ItemRelationships] WITH CHECK ADD CONSTRAINT [FK_ItemRelationships_ChildItems] FOREIGN KEY([ParentItemId])
REFERENCES [dbo].[Items] ([ItemId])
GO
ALTER TABLE [dbo].[ItemRelationships] CHECK CONSTRAINT [FK_ItemRelationships_ChildItems]
GO
ALTER TABLE [dbo].[ItemRelationships] WITH CHECK ADD CONSTRAINT [FK_ItemRelationships_ParentItems] FOREIGN KEY([ChildItemId])
REFERENCES [dbo].[Items] ([ItemId])
GO
ALTER TABLE [dbo].[ItemRelationships] CHECK CONSTRAINT [FK_ItemRelationships_ParentItems]
GO
ALTER TABLE [dbo].[Items] WITH CHECK ADD CONSTRAINT [FK_Items_Containers] FOREIGN KEY([ContainerId])
REFERENCES [dbo].[Containers] ([ContainerId])
ON UPDATE CASCADE
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Items] CHECK CONSTRAINT [FK_Items_Containers]
GO
USE [master]
GO
ALTER DATABASE [TestDatabase] SET READ_WRITE
GO
The following SQL Query correctly returns the immediate child items:
DECLARE #itemId BIGINT = 17;
SELECT
Items.ItemId
FROM
[TestDatabase].[dbo].[ItemRelationships]
INNER JOIN
[TestDatabase].[dbo].Items
ON
ItemRelationships.ChildItemId = Items.ItemId
INNER JOIN
[TestDatabase].[dbo].[Containers]
ON
Items.ContainerId = Containers.ContainerId
WHERE
ItemRelationships.ParentItemId = #itemId
AND
Items.ContainerId =
(
SELECT
BaseContainerId
FROM
[TestDatabase].[dbo].[Items]
INNER JOIN
[TestDatabase].[dbo].[Containers]
ON
Items.ContainerId = Containers.ContainerId
WHERE
Items.ItemId = #itemId
)
Results
Item Id 17 belongs to container 4. Container 4's base Container is container 2, Container 2's base container is container 1, and container 1's base container is NULL.
The above query returns ItemIds 13, 14 and 15 (which is correct) within container 2. However I need this query to automatically then look for a base container for container 2 and get all the ItemId's for the base items of Items 13, 14 and 15 (which should yield Item Ids 1 to 9 in this scenario).
Notes
As an item can be attached (through itemrelationships) to items from unrelated containers, the check for the current item(s) container's base container MUST be present.
If the ItemId passed is within a base container then the query should simply return the ItemId passed.
A CTE is preferred as the results will actually be used as part of another query against another table (but that is outside of the scope of this question).
I hope someone can help and thank you in advance for your efforts.
I am not sure I understand completely your model and the place of ItemRelationships table, so the query might require some tweaking - however it should give you an idea how to use recursive CTE.
DECLARE #itemID INT
Set #itemID = 17
;WITH CTE_Containers AS
(
SELECT c.ContainerId, c.BaseContainerId, i.ItemID AS ChildItemId, NULL AS ParentItemID, i.Name
FROM Items i
INNER JOIN Containers c ON i.ContainerId = c.ContainerId
WHERE i.ItemId = #itemID
UNION ALL
SELECT c.ContainerId, c.BaseContainerId, ir.ChildItemId, ir.ParentItemId, i.Name
FROM CTE_Containers cte
INNER JOIN dbo.Containers c ON cte.BaseContainerId = c.ContainerId
INNER JOIN dbo.ItemRelationships ir ON ir.ParentItemId = cte.ChildItemId
INNER JOIN dbo.Items i ON ir.ChildItemId = i.ItemID
)
SELECT * FROM CTE_Containers
As you may see - recursive CTEs consists of two parts. First (base) part - you select your row for given #itemID and in second (recursive) part you join your base part to tables to get child item.
This will run until there is nothing selected in recursive part - or some other condition you may impose is met.