How to calculate the degree of agreement by row comparisons in SQL Server? - sql

For a minimal, reproducible example (reprex) let's assume I have a database object (dbo) in a Microsoft SQL Server and I want to query things in T-SQL.
My dbo looks like this:
Animal-ID Marker-ID Allele1 Allele2
--------------------------------------------
1 OAR1 A G
1 OAR2 C C
1 OAR3 T G
2 OAR1 A C
2 OAR2 C C
2 OAR3 A C
What I would like to do is calculate an allele match percentage per Marker-ID across all Animal-IDs.
Given the dbo example from above the desired result looks like this:
Animal-ID-pair Marker-ID Match-percentage
--------------------------------------------
1-2 OAR1 50
1-2 OAR2 100
1-2 OAR3 0
So far, I tried the following approaches:
First I thought selecting individual rows is sufficient.
SELECT *
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY Animal-ID ASC) AS rownumber,
Animal-ID, Marker-ID,
Allele1, Allele2
FROM
dbo) AS foo
WHERE
rownumber BETWEEN 1 AND 3;
and then compare that to the range between 4 and 6.
The problem here is that, in my real and way lager data set, not all animal-ID pairs have the same number of rows, i.e. not the same number of markers.
That is why I thought grouping might be helpful:
SELECT
Animal-ID, Marker-ID,
Allele1, Allele2
FROM
dbo
WHERE
Animal-ID IN (SELECT Animal-ID FROM dbo
GROUP BY Animal-ID
HAVING COUNT(*) > 1);
but that does not allow me to do comparisons and/or calculations across groups.
Thus I would like to ask how to calculate the degree of agreement in the comparison of row pairs.

Sample data
create table genomes
(
AnimalId int,
MarkerId nvarchar(10),
Allele1 nvarchar(1),
Allele2 nvarchar(2)
)
insert into genomes (AnimalId, MarkerId, Allele1, Allele2) values
(1, 'OAR1', 'A', 'G'),
(1, 'OAR2', 'C', 'C'),
(1, 'OAR3', 'T', 'G'),
(2, 'OAR1', 'A', 'C'),
(2, 'OAR2', 'C', 'C'),
(2, 'OAR3', 'A', 'C'),
(3, 'OAR1', 'A', 'G'), --new sample Animal with less data (no OAR3)
(3, 'OAR2', 'C', 'G');
Solution
Select all unique animals cte_AllAnimals.
Select all unique markers cte_AllMarkers.
Combine every animal with every animal behind it a2.AnimalId > a1.AnimalId. This will give you all unique animal combinations.
Combine every pair with every marker cross join cte_AllMarkers.
This gives me:
with cte_AllMarkers as
(
select g.MarkerId
from genomes g
group by g.MarkerId
),
cte_AllAnimals as
(
select g.AnimalId
from genomes g
group by g.AnimalId
)
select convert(nvarchar(10), a1.AnimalId) + '-' +
convert(nvarchar(10), a2.AnimalId) as AnimalIdPair,
m.MarkerId,
case g1.Allele1 when g2.Allele1 then 50 else 0 end +
case g1.Allele2 when g2.Allele2 then 50 else 0 end as MatchPercentage
from cte_AllAnimals a1
join cte_AllAnimals a2
on a2.AnimalId > a1.AnimalId
cross join cte_AllMarkers m
left join genomes g1
on g1.AnimalId = a1.AnimalId
and g1.MarkerId = m.MarkerId
left join genomes g2
on g2.AnimalId = a2.AnimalId
and g2.MarkerId = m.MarkerId
order by a1.AnimalId,
a2.AnimalId,
m.MarkerId;
Result
AnimalIdPair MarkerId MatchPercentage
------------ -------- ---------------
1-2 OAR1 50
1-2 OAR2 100
1-2 OAR3 0
1-3 OAR1 100
1-3 OAR2 50
1-3 OAR3 0
2-3 OAR1 50
2-3 OAR2 50
2-3 OAR3 0
Fiddle to see it in action.

By Using SUBQUERY & STUFF
DECLARE #T TABLE(Animal_ID INT, Marker_ID CHAR(10) , Allele1 CHAR, Allele2 CHAR)
INSERT INTO #T VALUES
(1,'OAR1','A','G'),
(1,'OAR2','C','C'),
(1,'OAR3','T','G'),
(2,'OAR1','A','C'),
(2,'OAR2','C','C'),
(2,'OAR3','A','C')
SELECT * FROM #T
SELECT S.*,(ISNULL(S1.C,0)+ISNULL(S2.C,0))*100/LEN(Allele_Pair) AS Percentage
FROM (
SELECT STUFF((SELECT CONCAT('-' , Animal_ID ) FROM #T t1
WHERE t1.Marker_ID = t2.Marker_ID FOR XML PATH ('')), 1, 1, '') AS Animal_ID_Pair
,Marker_ID,
STUFF((SELECT CONCAT(Allele1,Allele2) FROM #T t1
WHERE t1.Marker_ID = t2.Marker_ID FOR XML PATH ('')), 1, 0, '') AS Allele_Pair
FROM #T t2
GROUP BY Marker_ID) S
LEFT JOIN (SELECT Marker_ID,Allele2,COUNT(Allele2) AS C FROm #T GROUP BY Allele2,Marker_ID HAVING COUNT(Allele2)>1) S1 ON S1.Marker_ID=S.Marker_ID
LEFT JOIN (SELECT Marker_ID,Allele1,COUNT(Allele1) AS C FROm #T GROUP BY Allele1,Marker_ID HAVING COUNT(Allele1)>1) S2 ON S2.Marker_ID=S.Marker_ID
Output:
Animal_ID_Pair Marker_ID Allele_Pair Percentage
1-2 OAR1 AGAC 50
1-2 OAR2 CCCC 100
1-2 OAR3 TGAC 0

A self-join does what you want -- with some arithmetic:
select t1.animal_id, t2.animal_id,
( case when t1.allele1 = t2.allele1 then 1.0 else 0 end +
case when t1.allele2 = t2.allele2 then 1.0 else 0 end +
) / 2.0 as match_percentage
from t t1 join
t t2
on t1.marker_id = t2.marker_id and
t1.animal_id < t2.animal_id;
Although it is easy enough to add new alleles into this. You can also express this as by unpivoting the alleles and aggregating:
with ta as (
select t.*,, v.*
from t cross apply
(values (1, allele1), (2, allele2)) v(allele, val)
)
select ta1.animal_id, ta2.animal_id, mta1.marker,
avg(case when ta1.val = ta2.val then 1.0 else 0 end) as match_percentage
from ta ta1 join
ta ta2
on ta1.marker_id = ta2.marker_id and
ta1.animal_id < ta2.animal_id
group by ta1.animal_id, ta2.animal_id;

Related

Common point in multiple polygons in SQL

i have two tables that contains a list geometry data
Ex. (0xE6100000010CFB24190B88E44A40AADDAB69817F3740)
i did the intersection of shapes between the two tables , now i'm trying to find a common point in all the intersected shapes
i tried to find the STCentroid() of each shape , but i can't find out how to find the common point in all of them
select p1.shape_data.STIntersection(p2.shape_data).STCentroid() as inter_geometry
from map_shapes p1
inner join areas_map_shapes p2 on p2.shape_data.STIntersects(p1.shape_data) = 1
where p2.shape_data.STIntersects(p1.shape_data) = 1
and p2.shape_id = 206
i tried also to aggregate all the intersected shapes
SELECT
geometry::UnionAggregate(ss.shape_data),
geometry::STGeomFromText( geometry::UnionAggregate(ss.shape_data).STCentroid().ToString(), 0).STY as lat,
geometry::STGeomFromText( geometry::UnionAggregate(ss.shape_data).STCentroid().ToString(), 0).STX as lon
FROM areas_map_shapes T
inner join map_shapes SS on SS.shape_data.STIntersects(T.shape_data) = 1
WHERE SS.shape_data.STIntersects(T.shape_data) = 1
AND T.shape_id = 206
and T.status = 1
and SS.status = 1
and T.country_id = 4
my problem is that i need to find the only one common point in all the shapes that intersects
adding image to represent what i got so far , this shows all the shapes the intersects with the main shape , i need to find a common point in all of them
Its hard to tell from your example because (as #nbk pointed out) its difficult to reproduce what you're asking for. That said, it looks like you're looking for the STIntersection function.
DECLARE #GeometryTable TABLE(
ID INT,
geom GEOMETRY
)
INSERT INTO #GeometryTable (ID, Geom) VALUES (1, GEOMETRY::STGeomFromText('POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))', 0))
INSERT INTO #GeometryTable (ID, Geom) VALUES (2, GEOMETRY::STGeomFromText('POLYGON((1 1, 1 3, 3 3, 3 1, 1 1))', 0))
INSERT INTO #GeometryTable (ID, Geom) VALUES (3, GEOMETRY::STGeomFromText('POLYGON((0 1, 0 3, 2 3, 2 1, 0 1))', 0))
SELECT
G1.geom.STIntersection(G2.geom).STIntersection(G3.geom)
FROM
#GeometryTable G1
INNER JOIN
#GeometryTable G2
ON
G1.geom.STIntersects(G2.geom) = 1
INNER JOIN
#GeometryTable G3
ON
G1.geom.STIntersects(G3.geom) = 1
AND G2.geom.STIntersects(G3.geom) = 1
WHERE
G1.ID = 1
AND G2.ID = 2
AND G3.ID = 3
Not sure there's an easy/fast way to do it. One idea is to use STIntersection to create a intersection polygon of all your areas in a recursive CTE:
drop table #t_geoms
create table #t_geoms (geom geometry, row_id int identity)
-- create some random data
insert into #t_geoms
select top 30 GEOMETRY::Point(ROW_NUMBER() OVER(ORDER BY object_id) * 0.01 + 10,ROW_NUMBER() OVER(ORDER BY object_id) * 0.01 + 10, 4326).STBuffer(3) x
from sys.objects
;with cte as (
select geom, row_id
from #t_geoms
where row_id = 1
union all
select g.geom.STIntersection(c.geom), g.row_id
from cte c
inner join #t_geoms g
ON g.row_id = c.row_id + 1
)
select top 1 geom, geom.STCentroid() AS centerPointOfIntersection
from cte
order by row_id desc
option(MAXRECURSION 0)
Note that if not all polygons actually intersect, you get an emptry geom

SQL Server loop through a table for every 5 rows

I need to write a stored procedure or table function to return a new data table as a new data source.
I wish to loop through the original table for every 5 rows base on the invoice ID column (it's possible not start from 1), the first 5 rows add to the left of the new table and the second 5 rows add to the right of the new table, the third 5 rows to the left and so on.
For example, Here is the original table:
Here is the expect table:
Thanks in advance!
declare #rowCount int = 5;
with cte as (
select *,( (IN_InvoiceID-1) / #rowCount ) % 2 group1
,( (IN_InvoiceID-1) / #rowCount ) group2
,IN_InvoiceID % #rowCount group3
from T
)
select * from cte
select T1.INID,T1.IN_InvoiceID,T1.IN_InvoiceAmount,T2.INID,T2.IN_InvoiceID,T2.IN_InvoiceAmount
from CTE T1
left join CTE T2 on T2.group1 = 1 and T1.group2 = T2.group2-1 and T1.group3 = T2.group3
where T1.group1 = 0
Test DDL
CREATE TABLE T
([INID] varchar(38), [IN_InvoiceID] int, [IN_InvoiceAmount] int)
;
INSERT INTO T
([INID], [IN_InvoiceID], [IN_InvoiceAmount])
VALUES
('DB3E17E6-35C5-41:121-93B1-F809BF6B2972', 1, 2999),
('3212F048-8213-4FCC-AB64-121485B77D4E43', 2, 3737),
('E3526373-A204-40F5-801C-7F8302A4E5E2', 3, 3175),
('76CC9C19-BF79-4E8A-8034-A33805AD3390', 4, 391),
('EC7A2FBC-B62D-4865-88DE-A8097975F125', 5, 1206),
('52AD3046-21331-4F0A-BD1D-67F232C54244', 6, 402),
('CA48F132-A9F5-4516-9E58-CDEE6644AAD1', 7, 1996),
('02E10C31-CAB2-4220-B66A-CEE5E67A9378', 8, 3906),
('98F1EEFF-B07A-4B65-87F4-E165264284DD', 9, 2575),
('91EBDD8B-B73C-470C-8900-DD66078483DB', 10, 2965),
('6E2490E5-C4DE-4833-877F-1590F7BDC1B8', 11, 1603),
('00985921-AC3C-4E3E-BAE1-7F58302F831A', 12, 1302)
;
Result:
Could you please check article Display Data in Multiple Columns using SQL showing with example case how a database developer can show the list of data rows in a columnar mode using Row_Number() function and mode arithmetic expression
You need to add additional columns from the same row that is different in the sample
Seems as if you want to split the table into 2 tables with alternating 5 rows. An easy way to do this would be:
Take data into a temp table having an extra column (lets say
grouping_id)
Update the grouping id so that each 5 rows have the same id. You can
use in_invoiceId % 5 (the nod function). After this step the first 5
rows will have grouping_id 0, next 5 will have 1, next will have 2
(assuming your invoice id is incremented +1 for all rows).
You can just do a normal select with where clause for odd and even grouping_id
Ideally, you can manage with the 2 tables Master and detail table.
But due to my curiosity, I am able to solve and give the answer as
Declare #table table(id int identity, invoice_id int)
; WITH Numbers AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n+1 <= 50
)
insert into #table SELECT n
FROM Numbers
Select (a.id )%5 ,* from #table a join #table b on a.id+5 = b.id and a.id != b.id
;WITH Numbers AS
(
SELECT n = 1, o = 5
UNION ALL
SELECT n + 10, o = o+10
FROM Numbers
WHERE n+1 <= 50
)
select a.id ParentId,a.invoice_id ParentInvoiceId, --b.n, b.o,
c.invoice_id childInvoiceID from #table a
join Numbers b on a.id between b.n and b.o
left join #table c on a.id + 5 = c.id
Here is my solution
First i create grps based on whether the in_invoiceid is divisible by 5 or not.(Ignore the remainders)
After that i create a category to indicate between alternative groups(ie by checking if the remainder is 0 or otherise)
Then its a matter of dense_ranking the records on the basis of the category field ordered by in_invoiceid
Lastly a join with category=1 rows with same dense_rank as those records in category=0
create table Invoicetable(IN_ID varchar(100), IN_InvoiceID int)
INSERT INTO Invoicetable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
with data
as (
select *
,(in_invoiceid-1)/5 as grp
,case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end as category
,dense_rank() over(partition by case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end
order by in_invoiceid) as rnk
from invoicetable a
)
select *
from data a
left join data b
on a.rnk=b.rnk
and b.category=0
where a.category=1
Here is db fiddle link.
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=287f101737c580ca271940764b2536ae
You may try with the following approach. Dividing the table is done with (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) which groups records in left and right groups.
CREATE TABLE #InvoiceTable(
IN_ID varchar(24),
IN_InvoiceID int
)
INSERT INTO #InvoiceTable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
WITH cte AS (
SELECT
IN_ID,
IN_InvoiceID,
CASE
WHEN (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) THEN 'L'
ELSE 'R'
END AS IN_Position
FROM #InvoiceTable
),
cteL AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'L'
),
cteR AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'R'
)
SELECT cteL.IN_ID, cteL.IN_InvoiceID, cteR.IN_ID, cteR.IN_InvoiceID
FROM cteL
LEFT JOIN cteR ON (cteL.IN_RowNumber = cteR.IN_RowNumber)
Output:
IN_ID IN_InvoiceID IN_ID IN_InvoiceID
2345-BCDE-6645-1DDF 1 2345-BCDE-6645-7DDF 6
2345-BCDE-6645-3DDF 2 2345-BCDE-6645-aDDF 7
2345-BCDE-6645-4DDF 3 2345-BCDE-6645-sDDF 8
2345-BCDE-6645-5DDF 4 2345-BCDE-6645-dDDF 9
2345-BCDE-6645-6DDF 5 2345-BCDE-6645-dDDF 10
2345-BCDE-6645-dDDF 11 NULL NULL
2345-BCDE-6645-dDDF 12 NULL NULL

SQL query with subqueries based on base values

Good morning; I have two tables that I am trying to work with to develop a single query with subquery and having issues if someone could help.
First Table - [MPA_Desc] - There is more data I parsed for example
MPAID Color Model Side
1 085 x1 R
2 777 x1 R
3 085 x1 L
4 777 x1 L
Second Table - [Paintsched] - There is more data I parsed for example
Lot MPAID Amount
1 1 100
2 2 250
3 4 100
4 2 100
I am trying to get this as my query result:
Color R L
085 100 0
777 350 100
This is the query and subquery I am using, It's incomplete and doesn't have the "LH" calculations in it because I want to get the RH right first:
Select distinct(mp.Color), rh.RH
from MPA_Desc MP
right join (Select MPA_Desc.MPAID, MPA_Desc.Color, nullif(sum(qty),0) as RH from PaintSched inner join MPA_Desc on PaintSched.MPAID = MPA_Desc.MPAID
where side = 'r' group by MPA_Desc.MPAID,MPA_Desc.Color) RH
ON mp.MPAID = rh.MPAID
where Model = 'x1'
But this however gives me the following result:
Color R
085 100
085 NULL
777 350
777 Null
I know it's from my full join but I need to show the Null's as "0" and I thought nullif would work but apparently not in a subquery. Secondly I need it to just show the List of color codes by model in the original query and seperate out the sums based on RH or LH parts. Any help would be great, thank you all!
You can use PIVOT
DECLARE #MPA_Desc TABLE (MPAID INT, Color VARCHAR(5), Model VARCHAR(5), Side VARCHAR(5))
INSERT INTO #MPA_Desc VALUES
(1 , '085', 'x1', 'R'),
(2 , '777', 'x1', 'R'),
(3 , '085', 'x1', 'L'),
(4 , '777', 'x1', 'L')
DECLARE #Paintsched TABLE (Lot INT, MPAID INT, Amount INT)
INSERT INTO #Paintsched VALUES
(1 ,1 , 100),
(2 ,2 , 250),
(3 ,4 , 100),
(4 ,2 , 100)
SELECT Color, ISNULL(R,0) R, ISNULL( L,0) L FROM
( SELECT Color, Side, Amount FROM #MPA_Desc D
INNER JOIN #Paintsched P ON D.MPAID = P.MPAID ) SRC
PIVOT (SUM(Amount) FOR Side IN ([R],[L])) PVT
Result:
Color R L
----- ----------- -----------
085 100 0
777 350 100
You need conditional aggregation :
select md.Color,
sum(case when Side = 'R' then ps.amount else 0 end),
sum(case when Side = 'L' then ps.amount else 0 end)
from MPA_Desc md inner join
Paintsched ps
on ps.MPAID = md.MPAID
group by md.Color;
Try the following (Its Dynamic) -:
Declare #sql varchar(MAX),#query nvarchar(MAX)
select #sql=stuff((select distinct ', sum(case when side='''+side+''' then amount else 0 end) as '+side from [MPA_Desc] FOR XML PATH ('')), 1, 1, '' )
set #query='select color,'+#sql+' from [MPA_Desc] m join [Paintsched] n on m.MPAID=n.MPAID group by color'
EXEC sp_sqlexec #query
SQL Server 2014

sql generate code based on three column values

I have three columns
suppose
row no column1 column2 column3
1 A B C
2 A B C
3 D E F
4 G H I
5 G H C
I want to generate code by combining these three column values
For Eg.
1)ABC001
2)ABC002
3)DEF001
4)GHI001
5)GHC001
by checking combination of three columns
logic is that
if values of three columns are same then like first time it shows 'ABC001'
and 2nd time it shows 'ABC002'
You can try this:
I dont know what you want for logic with 00, but you can add them manuel or let the rn decide for you
declare #mytable table (rowno int,col1 nvarchar(50),col2 nvarchar(50),col3 nvarchar(50)
)
insert into #mytable
values
(1,'A', 'B', 'C'),
(2,'A', 'B', 'C'),
(3,'D', 'E', 'F'),
(4,'G', 'H', 'I'),
(5,'G', 'H', 'C')
Select rowno,col1,col2,col3,
case when rn >= 10 and rn < 100 then concatcol+'0'+cast(rn as nvarchar(50))
when rn >= 100 then concatcol+cast(rn as nvarchar(50))
else concatcol+'00'+cast(rn as nvarchar(50)) end as ConcatCol from (
select rowno,col1,col2,col3
,Col1+col2+col3 as ConcatCol,ROW_NUMBER() over(partition by col1,col2,col3 order by rowno) as rn from #mytable
) x
order by rowno
My case when makes sure when you hit number 10 it writes ABC010 and when it hits above 100 it writes ABC100 else if its under 10 it writes ABC001 and so on.
Result
TSQL: CONCAT(column1,column2,column3,RIGHT(REPLICATE("0", 3) + LEFT(row_no, 3), 3))
You should combine your columns like below :
SELECT CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER(ORDER BY
(
SELECT NULL
)))+') '+DATA AS Data
FROM
(
SELECT column1+column2+column3+'00'+CONVERT(VARCHAR(MAX), ROW_NUMBER() OVER(PARTITION BY column1,
column2,
column3 ORDER BY
(
SELECT NULL
))) DATA
FROM <table_name>
) T;
Result :
1)ABC001
2)ABC002
3)DEF001
4)GHI001
5)GHC001
MySQL:
CONCAT(column1,column2,column3,LPAD(row_no, 3, '0'))
[you will need to enclose the 'row no' in ticks if there is a space in the name of the field instead of underscore.]

How to add and subtract value from previous rows based on condition

I have a table with values
Slno Type Amount
1 P 40
2 C 20
3 P 45
4 P 20
5 C 10
I want to get values for RESULT column.
Type Amount RESULT
P 40 40
C 20 20
P 45 65
P 20 85
C 10 75
If Type is C then value gets subtracted from previous value,
else if Type is P then value gets added to previous values.
This is what i've tried:
;WITH FINALMIDRESULT
AS (SELECT Type,
Value1,
Row_number()
OVER(
ORDER BY Slno ASC) rownum
FROM #midRes)
SELECT C1.Type,
C1.Value1,
CASE
WHEN C1.Type = 'C' THEN (SELECT Sum(Amount)
FROM FINALMIDRESULT c2
WHERE c2.rownum <= C1.rownum)
ELSE (SELECT Sum(Amount) - Sum(Amount)
FROM FINALMIDRESULT c2
WHERE c2.rownum <= C1.rownum)
END AS RESULT
FROM FINALMIDRESULT C1
This is the Result that i have got
Type Amount RESULT
P 40 0
C 20 60
P 45 0
P 20 0
C 10 135
You need to implement a seft INNER JOIN to sum all values with Slno less than the current value, like below:
;WITH OriginalData AS
( SELECT *
FROM
( VALUES
(1, 'P', 40),
(2, 'C', 20),
(3, 'P', 45),
(4, 'P', 20),
(5, 'C', 10)
) AS Temp(Slno, Type, Amount)
)
SELECT [Current].Type, [Current].Amount,
ISNULL(SUM(
CASE WHEN [Previous].Type = 'P'
THEN +[Previous].Amount
ELSE -[Previous].Amount
END),0) +
CASE WHEN [Current].Type = 'P'
THEN +[Current].Amount
ELSE -[Current].Amount
END Result
FROM OriginalData [Current]
LEFT JOIN OriginalData [Previous]
ON [Previous].Slno < [Current].Slno
GROUP BY [Current].Slno, [Current].Type, [Current].Amount
ORDER BY [Current].Slno
I think the biggest change you can make is to shift your mindset. When you think "previous values" you chose a procedural path which can be solved my any major programming language, but rapidly evolve to a cursor approach in SQL -- what isn't appropriate in this case.
When comes to SQL, you need to think in "sets", so you can drive your efforts to identify those data sets and combine them.
SELECT SlNo, Type, Amount,
Sum((Case when Type='C' then -1 else 1 END)*Amount) Over(Order by SlNo) Result
FROM TableName