How to calculate Opening and Closing Balance in Transaction - sql

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

Related

Get multiple records between particular start and end point in the table

I have table data like below
In the given table, some records are outdated data that is generated by a bug in a system.
I want to remove all the outdated records
Correct data condition:
if itemStatus rows start and end from status 4 and 6 without 13 or 14 status then it is correct. I do not need to delete that row.
Example: Id 18 to 20 that are correct data.
Note: Id 21 and 22 is correct because status 13,14 start after status 6 (id 20)
Incorrect data conditions:
If itemStatus row starts and ends from status 4 and 6, with 13 and 14 status. Then it is incorrect data and I have to delete that from a DB.
Example: 24 and 25 then 29 and 30
My query
IF OBJECT_ID('tempdb..#TempResult') IS NOT NULL DROP TABLE #TempResult
CREATE TABLE #TempResult (
Id int
)
select ItemId
into #TempItemGroup
from Item
group by itemid
declare #SelectedItemId int
while exists (select ItemId from #TempItemGroup)
begin
select #SelectedItemId = (select top 1 ItemId
from #TempItemGroup
order by ItemId asc
)
IF OBJECT_ID('tempdb..#TempSingleItemGroup') IS NOT NULL DROP TABLE #TempSingleItemGroup
SELECT i1.*
into #TempSingleItemGroup
FROM Item i1
WHERE i1.[Id] >= ( SELECT TOP 1 [Id] FROM Item
WHERE [ItemStatus] = 4 and ItemId = #SelectedItemId )
AND i1.[Id] <= ( SELECT TOP 1 [Id] FROM Item
WHERE [ItemStatus] = 6 and ItemId = #SelectedItemId )
INSERT INTO #TempResult (Id) (SELECT Id FROM #TempSingleItemGroup where ItemStatus = 13 or ItemStatus = 14)
IF OBJECT_ID('tempdb..#TempSingleItemGroup') IS NOT NULL DROP TABLE #TempSingleItemGroup
delete #TempItemGroup
where ItemId = #SelectedItemId
end
-- Delete or do other operation if required
IF(EXISTS(SELECT count(Id) FROM #TempResult))
BEGIN
-- write a query to delete the data
select * from #TempResult
END
IF OBJECT_ID('tempdb..#TempResult') IS NOT NULL DROP TABLE #TempResult
IF OBJECT_ID('tempdb..#TempItemGroup') IS NOT NULL DROP TABLE #TempItemGroup
Expected result
There are some differences between screenshot and actual table data that you can find at the end of this question
Screenshot: 24,25,29,30
Table data: 13,14,15 ,26,27, 31,32
I have two queries
My above query is only getting first status which have 4 and 6
status, example Id 24 and 25, and I am unable to get next wrong
data for 29 and 30
I am using while loop that may not a good
practices. Please suggest me a better way to write a query.
Table schema and data
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Item](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ItemId] [int] NOT NULL,
[ItemStatus] [int] NOT NULL,
CONSTRAINT [PK_Item] 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
SET IDENTITY_INSERT [dbo].[Item] ON
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (1, 23, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (2, 23, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (3, 23, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (4, 23, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (5, 23, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (6, 23, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (7, 23, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (8, 23, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (9, 23, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (10, 45, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (11, 45, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (12, 45, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (13, 45, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (14, 45, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (15, 45, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (16, 45, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (17, 45, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (18, 25, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (19, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (20, 25, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (21, 25, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (23, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (24, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (25, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (26, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (27, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (28, 25, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (29, 25, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (30, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (31, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (32, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (33, 24, 6)
GO
SET IDENTITY_INSERT [dbo].[Item] OFF
GO
My reading of your requirement is this:
For each itemId, find rows with itemStatus in (13, 14) that lie between a row with the same itemId and itemStatus 4, and a row with the same itemid and itemStatus 6, in order of id ascending.
Your current requirements do not state whether we should return rows with item status in (13, 14) where there is a prior 4 row, but no subsequent 6 row (the "end" of the itemid group is reached), or what to do if there is a subsequent 6 row, but no prior 4 row (the "start" of the itemid group is reached).
I make the following interpretation: itemStatus 4 opens a "status block". itemStatus 6 closes a "status block". There should be no rows with status 13 or 14 in an open block.
If that is a correct interpretation, all we really need to do is find rows with item status 13 or 14, and then find the nearest prior row in order of id descending with a status of either 4 or 6. If the status of that prior row is 4 then the rows are in an open block. If the status of that prior row is 6, or no prior row can be found, then the rows are not in an open block.
Note: In the comments you mentioned expected results including id 31 and 32. But your sample data does not include any row with id 31 or 32. You have now provided these rows and I have added them to my data.
create table #items
(
Id int primary key,
ItemId int not null,
ItemStatus int not null,
);
insert #items values
(2, 23, 4),
(3, 23, 7),
(4, 23, 6),
(5, 23, 13),
(6, 23, 14),
(7, 23, 4),
(8, 23, 6),
(9, 23, 3),
(10, 45, 2),
(11, 45, 4),
(12, 45, 7),
(13, 45, 13),
(14, 45, 14),
(15, 45, 13),
(16, 45, 6),
(17, 45, 3),
(18, 25, 2),
(19, 25, 4),
(20, 25, 7),
(21, 25, 6),
(23, 25, 13),
(24, 25, 14),
(25, 25, 4),
(26, 25, 13),
(27, 25, 14),
(28, 25, 6),
(29, 25, 3),
(30, 25, 4),
(31, 25, 13),
(32, 25, 14),
(33, 24, 6);
select item13or14.*
from #items item13or14
cross apply (
select top 1 itemStatus
from #items
where itemId = item13or14.itemId
and id < item13or14.id
and itemStatus in (4, 6)
order by id desc
) prior4or6
where item13or14.itemStatus in (13, 14)
and prior4or6.itemStatus = 4;
/* produces:
Id ItemId ItemStatus
13 45 13
14 45 14
15 45 13
26 25 13
27 25 14
31 25 13
32 25 14
*/

SSMS - Seeking help writing a select statement to return duplicates in SQL [Example included]

I am trying to implement validation to check whether the same FX rate has been entered in reverse order for the same date. Users would populate a spreadsheet consisting of five columns:
Date
From Currency
To Currency
Rate
Multiply/Divide
The user would add data into a spreadsheet to import through the system, below is a mock example of how the data would look in the table in the database:
I have the structure of the validation in place, I am just wondering how I would write a select statement in order to return all rows which:
Have both directions for the same date, as shown in the first two rows in the image above^. USD & GBP both have Multiply (M) and Divide (D) for the 01/02/2023, therefore I would like to return these rows as to fail the validation, none of the other rows in the example should be returned as they are valid.
I thought I'd include a script to create the table shown in the image above which includes the exact data shown. I am hoping to return the first two rows from the table above
CREATE TABLE [tblFXRates](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Date] [datetime] NOT NULL,
[FromCurrency] [nvarchar](3) NOT NULL,
[ToCurrency] [nvarchar](3) NOT NULL,
[Rate] [decimal](19, 2) NOT NULL,
[Divide/Multiply] [nvarchar](1) NOT NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [tblFXRates] ON
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (1, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'USD', N'GBP', CAST(1.30 AS Decimal(19, 2)), N'M')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (2, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'GBP', N'USD', CAST(1.30 AS Decimal(19, 2)), N'D')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (3, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'GBP', N'EUR', CAST(1.11 AS Decimal(19, 2)), N'M')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (4, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'GBP', N'GBP', CAST(1.00 AS Decimal(19, 2)), N'M')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (5, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'GBP', N'GBP', CAST(1.00 AS Decimal(19, 2)), N'D')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (6, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'USD', N'USD', CAST(1.00 AS Decimal(19, 2)), N'M')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (7, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'USD', N'USD', CAST(1.00 AS Decimal(19, 2)), N'D')
GO
INSERT [tblFXRates] ([ID], [Date], [FromCurrency], [ToCurrency], [Rate], [Divide/Multiply]) VALUES (8, CAST(N'2023-02-01T00:00:00.000' AS DateTime), N'EUR', N'GBP', CAST(0.90 AS Decimal(19, 2)), N'M')
GO
SET IDENTITY_INSERT [tblFXRates] OFF
GO
Edit:
this will do:
assumig we want only those with different FromCurrency & ToCurrency, but of course you can change this.
SELECT
tbl1.*
FROM
test.tblfxrates tbl1
inner join
test.tblfxrates tbl2 on tbl2.FromCurrency = tbl1.ToCurrency and tbl2.ToCurrency = tbl1.FromCurrency and tbl1.FromCurrency <> tbl1.ToCurrency and tbl1.Date = tbl2.Date
where
tbl1.DivideMultiply <> tbl2.DivideMultiply
order by
tbl1.ID
it will return the first 2 lines

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:

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.

Difference between TOP X and Row_Number()

Recently I've faced weird issue. I have two simple queries where one of them uses TOP X and other one does the same by using ROW_NUMBER and then selects items with rowNumber between 1 and X, and both of them ordered by same column, but the result is completely different.
For example, let's say we have a simple DB as below with some dummy data:
CREATE TABLE [dbo].[Test](
[Id] [int] IDENTITY(1,1) NOT NULL,
[NDate] [datetime] NOT NULL,
CONSTRAINT [PK_Test] 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]
SET IDENTITY_INSERT [dbo].[Test] ON
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (1, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (2, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (3, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (4, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (5, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (6, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (7, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (8, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (9, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (10, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (11, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (12, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (13, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (14, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (15, '2011-08-21 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (16, '2011-08-21 00:00:00.000')
SET IDENTITY_INSERT [dbo].[Test] OFF
Now if we perform below queries, we will get different results. If we use TOP 10, we will get below result:
SELECT TOP 10 [Id],[NDate] FROM [Test] order by NDate desc
RESULT=>
Id NDate
4 2011-08-24 00:00:00.000
3 2011-08-24 00:00:00.000
2 2011-08-24 00:00:00.000
1 2011-08-24 00:00:00.000
11 2011-08-21 00:00:00.000
10 2011-08-21 00:00:00.000
9 2011-08-21 00:00:00.000
8 2011-08-21 00:00:00.000
7 2011-08-21 00:00:00.000
6 2011-08-21 00:00:00.000
select id,NDate from (
select ROW_NUMBER() over (order by NDate DESC) as RNumber, Id,NDate
from Test) as t
where RNumber between 1 and 10
RESULT=>
id NDate
1 2011-08-24 00:00:00.000
2 2011-08-24 00:00:00.000
3 2011-08-24 00:00:00.000
4 2011-08-24 00:00:00.000
5 2011-08-21 00:00:00.000
6 2011-08-21 00:00:00.000
7 2011-08-21 00:00:00.000
8 2011-08-21 00:00:00.000
9 2011-08-21 00:00:00.000
10 2011-08-21 00:00:00.000
The issue is, When you are using LINQ to SQL, and you want to do pagination, generated query for selecting first page will be TOP X, while for the other pages will be using ROW_NUMBER, and the result is, some items will never appear in the listing.
You need to implement a secondary sorting.
Example:
select id,NDate from (
select ROW_NUMBER() over (order by NDate DESC, id) as RNumber, Id,NDate -- Note: NDate DESC, id
from Test) as t
where RNumber between 1 and 10
Example:
SELECT TOP 10 [Id],[NDate] FROM [Test] order by NDate desc, ID -- Note: NDate DESC, id
Otherwise, you might as well expect random records per unique NDate.
If you want the same records for each query, you have to specify that secondary sort column.
I agree with #hamlin11, but putting it more simply :-)
In the first example you are sorting by
order by NDate desc
/* to get same results as example 2, change this to
order by NDate desc, id
*/
and in the second by
order by NDate DESC, id
In the second example you are sorting by Id, that is why the ID column is in order, you haven't done this in the first example.
It might be better to use data like this, so you can see more clearly what is happening:
/****** Object: Table [dbo].[Test] Script Date: 08/27/2011 07:56:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Test](
[Id] [int] IDENTITY(1,1) NOT NULL,
[NDate] [datetime] NOT NULL,
CONSTRAINT [PK_Test] 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 IDENTITY_INSERT [dbo].[Test] ON
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (1, '2011-08-10 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (2, '2011-08-11 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (3, '2011-08-12 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (4, '2011-08-13 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (5, '2011-08-14 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (6, '2011-08-15 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (7, '2011-08-16 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (8, '2011-08-31 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (9, '2011-08-30 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (10, '2011-08-29 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (11, '2011-08-28 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (12, '2011-08-27 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (13, '2011-08-26 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (14, '2011-08-25 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (15, '2011-08-24 00:00:00.000')
INSERT [dbo].[Test] ([Id], [NDate]) VALUES (16, '2011-08-23 00:00:00.000')
SET IDENTITY_INSERT [dbo].[Test] OFF