Sql Recursion via CTE over LINQ SelectMany - sql

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.

Related

How to calculate Opening and Closing Balance in Transaction

I need to Calculate the Opening and closing Balance Transaction. I have Three Tables OB, Purchase, and Usage. The unique Key of all tables is Product id. I need a stored procedure for blow results. Based on the Product id selected need to calculate the opening and closing balance.
Table Structure: Below code contains Table and sample data
USE [BMC]
GO
/****** Object: Table [dbo].[TBLProductOB] Script Date: 08-07-2021 01:20:11 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBLProductOB](
[Skey] [int] IDENTITY(1,1) NOT NULL,
[EntryDate] [date] NULL,
[Productid] [int] NULL,
[ProductOB] [decimal](12, 3) NULL,
CONSTRAINT [PK_TBLProductOB] PRIMARY KEY CLUSTERED
(
[Skey] 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].[TBLProductPurchase] Script Date: 08-07-2021 01:20:11 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBLProductPurchase](
[Skey] [int] IDENTITY(1,1) NOT NULL,
[Entrydate] [date] NULL,
[Productid] [int] NULL,
[P_Purchase] [decimal](12, 3) NULL,
CONSTRAINT [PK_TBLProductPurchase] PRIMARY KEY CLUSTERED
(
[Skey] 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].[TBLProductUsage] Script Date: 08-07-2021 01:20:11 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBLProductUsage](
[Skey] [int] IDENTITY(1,1) NOT NULL,
[Entrydate] [date] NULL,
[Productid] [int] NULL,
[P_Usage] [decimal](12, 3) NULL,
CONSTRAINT [PK_TBLProductUsage] PRIMARY KEY CLUSTERED
(
[Skey] 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].[TBLProductOB] ON
INSERT [dbo].[TBLProductOB] ([Skey], [EntryDate], [Productid], [ProductOB]) VALUES (4, CAST(N'2021-04-01' AS Date), 3, CAST(100.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductOB] ([Skey], [EntryDate], [Productid], [ProductOB]) VALUES (6, CAST(N'2021-04-01' AS Date), 1, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductOB] ([Skey], [EntryDate], [Productid], [ProductOB]) VALUES (7, CAST(N'2021-04-01' AS Date), 2, CAST(150.000 AS Decimal(12, 3)))
SET IDENTITY_INSERT [dbo].[TBLProductOB] OFF
SET IDENTITY_INSERT [dbo].[TBLProductPurchase] ON
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (1, CAST(N'2021-07-06' AS Date), 3, CAST(100.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (7, CAST(N'2021-07-01' AS Date), 3, CAST(50.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (8, CAST(N'2021-07-15' AS Date), 3, CAST(50.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (9, CAST(N'2021-07-01' AS Date), 1, CAST(1.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (10, CAST(N'2021-07-03' AS Date), 1, CAST(1.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (11, CAST(N'2021-07-05' AS Date), 1, CAST(3.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (12, CAST(N'2021-07-01' AS Date), 2, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (13, CAST(N'2021-07-02' AS Date), 2, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (14, CAST(N'2021-07-05' AS Date), 2, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductPurchase] ([Skey], [Entrydate], [Productid], [P_Purchase]) VALUES (15, CAST(N'2021-07-01' AS Date), 5, CAST(10.000 AS Decimal(12, 3)))
SET IDENTITY_INSERT [dbo].[TBLProductPurchase] OFF
SET IDENTITY_INSERT [dbo].[TBLProductUsage] ON
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (7, CAST(N'2021-07-01' AS Date), 3, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (8, CAST(N'2021-07-02' AS Date), 3, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (9, CAST(N'2021-07-08' AS Date), 3, CAST(10.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (10, CAST(N'2021-07-15' AS Date), 3, CAST(30.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (11, CAST(N'2021-07-01' AS Date), 2, CAST(2.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (12, CAST(N'2021-07-02' AS Date), 2, CAST(2.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (13, CAST(N'2021-07-03' AS Date), 2, CAST(2.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (14, CAST(N'2021-07-05' AS Date), 2, CAST(2.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (16, CAST(N'2021-07-01' AS Date), 1, CAST(2.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (17, CAST(N'2021-07-03' AS Date), 1, CAST(3.000 AS Decimal(12, 3)))
INSERT [dbo].[TBLProductUsage] ([Skey], [Entrydate], [Productid], [P_Usage]) VALUES (18, CAST(N'2021-07-04' AS Date), 2, CAST(2.000 AS Decimal(12, 3)))
SET IDENTITY_INSERT [dbo].[TBLProductUsage] OFF
Just tried: I don't know how to write a stored procedure for transaction-based. I was simply getting all the table results with the help of union.
Declare #1stOpeningBalance decimal(12,3)= (select ProductOB from TBLProductOB where Productid=2)
Select a.Entrydate,a.Productid,Lag(((Sum(a.Ob)+sum(a.Purchase))-sum(a.Usage)),1,#1stOpeningBalance) over (order by Entrydate asc) as Ob, sum(a.Purchase) as Purchase,(Sum(a.Ob)+sum(a.Purchase)) as Total, Sum(a.Usage) as Usage, ((Sum(a.Ob)+sum(a.Purchase))-sum(a.Usage)) as Cb from
(
select Entrydate,Productid,0 as Ob,Sum(Isnull(P_Purchase,0.000)) as Purchase,0 as Usage from TBLProductPurchase
group by EntryDate,Productid
union all
select Entrydate,Productid,0 as Ob,0 as Purchase,Sum(Isnull(P_Usage,0.000)) as Usage from TBLProductUsage
group by EntryDate,Productid
) as a
where Entrydate between '2021-07-01' and '2021-07-05' and Productid=2
group by a.EntryDate,a.Productid
Required Output Result:
You can use LEAD or LAG function to do this
Make sure you have only one record for the entry date. If you have multiple entries on the same date, you have to use a unique key to order
Assumption: you can calculate the closing balance and only opening balance needed to calcuate and need a system starting opening balance
Create table #temp_TBL(EntryDate date,Purchase decimal(18,2),total decimal(18,2),Usage decimal(18,2), CB decimal(18,2))
Declare #1stOpeningBalance decimal(18,2)=150
Insert into #temp_TBL (EntryDate,Purchase,total,Usage,CB)
values
('2021-07-01',10,160,2,158),
('2021-07-02',10,168,2,166),
('2021-07-03',0,166,2,164),
('2021-07-04',0,164,2,162),
('2021-07-05',10,172,2,170)
select EntryDate
,Lag(CB,1,#1stOpeningBalance) over (order by EntryDate ASC) as OB
,Purchase,total,Usage,CB from #temp_TBL
Output

Get Nested JSON from SQL table

Below is my table data looks like.
Below is SQL Query for Create table
CREATE TABLE [dbo].[CategoryMaster](
[CategoryId] [int] NOT NULL,
[ParentId] [int] NULL,
[Name] [varchar](50) NULL,
CONSTRAINT [PK_CategoryMaster] PRIMARY KEY CLUSTERED
(
[CategoryId] 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
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (1, NULL, N'Toys & Games')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (2, 1, N'Art And Crafts')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (3, 1, N'Baby & Toddler Toys')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (4, 1, N'Bikes, Trikes & Ride-Ons')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (5, 2, N'Aprons & Smocks')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (6, 2, N'Blackboards & Whiteboards')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (7, 2, N'Clay & Dough')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (8, 1, N'Pretend Play')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (9, 8, N'Kitchen Toys')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (10, 9, N'Cooking Appliances')
GO
INSERT [dbo].[CategoryMaster] ([CategoryId], [ParentId], [Name]) VALUES (11, 9, N'Cookware')
GO
ALTER TABLE [dbo].[CategoryMaster] WITH CHECK ADD CONSTRAINT [FK_CategoryMaster_CategoryMaster] FOREIGN KEY([ParentId])
REFERENCES [dbo].[CategoryMaster] ([CategoryId])
GO
ALTER TABLE [dbo].[CategoryMaster] CHECK CONSTRAINT [FK_CategoryMaster_CategoryMaster]
GO
I had tried many queries but not able to get desired result. can anyone please help me out from this situation?
I want output like below.
You can create a recursive function like below:
CREATE FUNCTION Create_Json(#CategoryId INT, #IsRoot INT)
RETURNS VARCHAR(MAX)
BEGIN
DECLARE #Json NVARCHAR(MAX) = '{}', #Name NVARCHAR(MAX), #Children NVARCHAR(MAX)
SET #Json = (SELECT P.[Name],JSON_QUERY(dbo.Create_Json(P.CategoryId, 2) ) AS Children
FROM [dbo].[CategoryMaster] AS P
WHERE P.ParentId = #CategoryId
FOR JSON AUTO);
IF(#IsRoot = 1)
BEGIN
SELECT #Name = P.[Name] FROM [dbo].[CategoryMaster] AS P WHERE P.CategoryId = #CategoryId
SET #Json = '"result": {"Name":"' + #Name + '","Children":' + CAST(#Json AS NVARCHAR(MAX)) + '}'
SET #IsRoot = 0
END
RETURN #Json
END
and call it like:
select dbo.Create_Json(1, 1)
Please find the db<>fiddle here.

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:

Using SQL Server Management Studio, Why does SQL Server not retrieve existing values?

I use SQL Server Management Studio with SQL Server 2016.
When I run this SQL statement, it doesn't return any values, even though the record is already there. Could you help please? What is the problem?
My query:
select *
from device_os
where name = '4.2 (Jelly Bean)'
This is the script for the table creation:
USE [my_database]
GO
/****** Object: Table [dbo].[device_os] Script Date: 08-Nov-17 3:49:11 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[device_os](
[id] [smallint] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NOT NULL,
CONSTRAINT [PK_device_os] 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
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[device_os] ON
INSERT [dbo].[device_os] ([id], [name]) VALUES (114, N'4.4.2')
INSERT [dbo].[device_os] ([id], [name]) VALUES (115, N'2.3.64.14.14.1')
INSERT [dbo].[device_os] ([id], [name]) VALUES (116, N'6.1.5')
INSERT [dbo].[device_os] ([id], [name]) VALUES (117, N'4.0.0')
INSERT [dbo].[device_os] ([id], [name]) VALUES (118, N'4.4.2 KOT49H.H1')
INSERT [dbo].[device_os] ([id], [name]) VALUES (119, N'unknown')
INSERT [dbo].[device_os] ([id], [name]) VALUES (120, N'CrunKeD! Galaxy Ace Ventura')
INSERT [dbo].[device_os] ([id], [name]) VALUES (121, N'7.0.5')
INSERT [dbo].[device_os] ([id], [name]) VALUES (122, N'7.0.6')
INSERT [dbo].[device_os] ([id], [name]) VALUES (123, N'6.1.6')
INSERT [dbo].[device_os] ([id], [name]) VALUES (124, N'4.4.2 (0Din Edition)')
INSERT [dbo].[device_os] ([id], [name]) VALUES (132, N'7.1.1')
INSERT [dbo].[device_os] ([id], [name]) VALUES (133, N'OS')
INSERT [dbo].[device_os] ([id], [name]) VALUES (134, N'4.2 (Jelly Bean)')
INSERT [dbo].[device_os] ([id], [name]) VALUES (135, N'5.0.0.1036')
INSERT [dbo].[device_os] ([id], [name]) VALUES (136, N'7.1.0.285')
INSERT [dbo].[device_os] ([id], [name]) VALUES (137, N'6.0.0.756')
INSERT [dbo].[device_os] ([id], [name]) VALUES (138, N'ICS Powered By PMP Engine')
INSERT [dbo].[device_os] ([id], [name]) VALUES (139, N'6.0.0.668')
SET IDENTITY_INSERT [dbo].[device_os] OFF
Screenshots for the problem :
select * without condition and the record is there 'highlighted in yellow'
When I add a condition on the value '4.2 (Jelly Bean)' no result there
The query at the top of your post should work:
select *
from device_os where
[name] = '4.2 (Jelly Bean)'
If it doesn't, you may have leading spaces. Note, trailing spaces usually doesn't matter for equality operators. Leading spaces do. If you think you may have them, try:
select *
from device_os
where ltrim([name]) = '4.2 (Jelly Bean)'
The only other thing it could be is you think you have a space in the name but you don't. Use this to see:
select *
from device_os
where ltrim([name]) = '4.2(Jelly Bean)' --space removed from between 2 and (
ONLINE DEMO
There are two spaces in there
'4.2 (Jelly Bean)'
Adding a separator to make it clear :
'4.2 | (Jelly Bean)'
Given this table :
declare #device_os table ([id] [smallint] NOT NULL,[name] [varchar](50) NOT NULL)
INSERT #device_os ([id], [name])
VALUES
(114, N'4.4.2'),
(115, N'2.3.64.14.14.1'),
(116, N'6.1.5'),
(117, N'4.0.0'),
(118, N'4.4.2 KOT49H.H1'),
(119, N'unknown'),
(120, N'CrunKeD! Galaxy Ace Ventura'),
(121, N'7.0.5'),
(122, N'7.0.6'),
(123, N'6.1.6'),
(124, N'4.4.2 (0Din Edition)'),
(132, N'7.1.1'),
(133, N'OS'),
(134, N'4.2 (Jelly Bean)'),
(135, N'5.0.0.1036'),
(136, N'7.1.0.285'),
(137, N'6.0.0.756'),
(138, N'ICS Powered By PMP Engine'),
(139, N'6.0.0.668')
The original query won't return anything:
select * from #device_os
where name = '4.2 (Jelly Bean)'
id name
--- -----------------
Using two spaces between 4.2 and Jelly Bean though will return the record:
select * from #device_os
where name = '4.2 (Jelly Bean)'
id name
--- -----------------
134 4.2 (Jelly Bean)
I found the root cause of the problem after much trials.
It looks like special characters were inserted when the data was initially inserted and it is not appear in the SSMS. I did the following:-
update this record in the database table:-
update device_os set name='4.2 (Jelly Bean)' where id=134
Run the same select statement again:-
select *
from device_os where
[name] = '4.2 (Jelly Bean)'
Now, it is working...
Thanks every body...

Select from 3 tables (one to many, many to one relationship)

I have 3 tables:
Recipes (1 --- * ) Ingridients ( *---1) Products. I need to obtain Recipes that contains products in a given list or products that are not in list but have a specific flag set. I have a flag in product table (bool). So where clause looks like:
WHERE Product.Caption IN ('A', 'B', 'C') OR (Product.Caption NOT IN ('A', 'B', 'C') AND Product.Flag=TRUE)
Important is: I do not need recipes that contain products in list and also contain other products (not in list and flag is false).
Bellow is an example database dump for MSSQL:
USE [master]
IF EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = N'movedb') ALTER DATABASE [movedb] SET SINGLE_USER With ROLLBACK IMMEDIATE
IF EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = N'movedb') DROP DATABASE [movedb]
IF NOT EXISTS (SELECT * FROM master.dbo.sysdatabases WHERE name = N'movedb') CREATE DATABASE [movedb]
USE [movedb]
--
-- Table structure for table 'Ingridients'
--
IF object_id(N'Ingridients', 'U') IS NOT NULL DROP TABLE [Ingridients]
CREATE TABLE [Ingridients] (
[Id] INT NOT NULL IDENTITY,
[Quantity] INT DEFAULT 0,
[IdProduct] INT DEFAULT 0,
[IdRecipe] INT DEFAULT 0,
PRIMARY KEY ([Id])
)
SET IDENTITY_INSERT [Ingridients] ON
GO
--
-- Dumping data for table 'Ingridients'
--
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (1, 0, 1, 2)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (2, 0, 3, 2)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (3, 0, 4, 2)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (4, 0, 6, 2)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (5, 0, 2, 3)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (6, 0, 4, 3)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (7, 0, 8, 3)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (8, 0, 1, 4)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (9, 0, 6, 4)
INSERT INTO [Ingridients] ([Id], [Quantity], [IdProduct], [IdRecipe]) VALUES (10, 0, 5, 4)
-- 10 records
SET IDENTITY_INSERT [Ingridients] OFF
GO
--
-- Table structure for table 'Products'
--
IF object_id(N'Products', 'U') IS NOT NULL DROP TABLE [Products]
CREATE TABLE [Products] (
[Id] INT NOT NULL IDENTITY,
[Caption] NVARCHAR(255),
[EasyToFind] BIT DEFAULT 0,
PRIMARY KEY ([Id])
)
SET IDENTITY_INSERT [Products] ON
GO
--
-- Dumping data for table 'Products'
--
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (1, N'ProductA', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (2, N'ProductB', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (3, N'ProductC', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (4, N'ProductD', -1)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (5, N'ProductE', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (6, N'ProductF', -1)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (7, N'ProductG', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (8, N'ProductH', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (9, N'ProductI', 0)
INSERT INTO [Products] ([Id], [Caption], [EasyToFind]) VALUES (10, N'ProductJ', 0)
-- 10 records
SET IDENTITY_INSERT [Products] OFF
GO
--
-- Table structure for table 'Recipes'
--
IF object_id(N'Recipes', 'U') IS NOT NULL DROP TABLE [Recipes]
CREATE TABLE [Recipes] (
[Id] INT NOT NULL IDENTITY,
[Caption] NVARCHAR(255),
PRIMARY KEY ([Id])
)
SET IDENTITY_INSERT [Recipes] ON
GO
--
-- Dumping data for table 'Recipes'
--
INSERT INTO [Recipes] ([Id], [Caption]) VALUES (2, N'RecipeA')
INSERT INTO [Recipes] ([Id], [Caption]) VALUES (3, N'RecipeB')
INSERT INTO [Recipes] ([Id], [Caption]) VALUES (4, N'RecipeC')
-- 3 records
SET IDENTITY_INSERT [Recipes] OFF
GO
Example:
If I search for ProductA and ProductE it should give me only RecipeC
Right now I have something like this for MySQL ( it is not final. I can only operate with Ids, I neet somehow to change it to work only with product captions and adapt for MSSQL)
SELECT
*
FROM
recipes AS r
INNER JOIN
ingridients i ON i.IdRecipe = r.Id
WHERE
i.IdProduct IN (1 , 5, 6)
GROUP BY r.Id
HAVING COUNT(*) = (SELECT
COUNT(*)
FROM
ingridients AS ing
WHERE
ing.IdRecipe = r.Id);
The following sql fetches the recipes that contains no products other than those in the list or having P.EasyToFind=-1.
select *
From Recipes
Where Id not in
(
select IdRecipe
from Ingridients I
inner join Products P ON I.IdProduct = P.Id
where P.Caption NOT IN ('ProductA','ProductE')
and P.EasyToFind=0
)
It works by having an inner query that identifies the unwanted ingredients and fetching the recipes that does not match any of them.