Table containing duplicates needs a mass update - sql

I've got a table containing data like this
transactionCode
column2
column3
column4
wCode
aCode
column7
column8
column9
column10
liNumber
7938636
2
INVOICE
NULL
1
MZ690577
2021-01-28
NULL
2021-01-28
1
6
7938636
2
INVOICE
NULL
1
MD191807
2021-01-28
NULL
2021-01-28
1
4
7938631
2
INVOICE
NULL
1
MZ320771
2021-01-28
NULL
2021-01-28
1
1
7938631
2
INVOICE
NULL
1
7803A112
2021-01-28
NULL
2021-01-28
4
2
7938576
2
INVOICE
NULL
1
8201A216
2021-01-29
NULL
2021-01-29
1
1
7938598
2
INVOICE
NULL
1
SP046271
2021-01-29
NULL
2021-01-29
1
14
I've also got a script like this which finds the duplicates for me
WITH cte
AS (SELECT transactionid,
aCode,
liNumber,
wCode,
RN = Row_number()
OVER(
partition BY
transactionid,
aCode,
liNumber,
wCode
ORDER BY
transactionid)
FROM duplicates)
SELECT * FROM cte
WHERE RN > 1;
When running that script the data shown is in a format like this..
transactionID
aCode
liNumber
wCode
RN
1012751
DISCOUNT
9
1
2
I can then search for that aCode or transactionID in the duplicates table to see how many there are. So far in my duplicates table, that script returns a total of 34,791 rows. Note, items that have the same liNumber needs to be changed.
My ask is, how do I go about doing this with this large amount of data?
For example,
Transaction 7938636 might have 5 rows. All with the same wCode and the same aCode BUT the liNumber goes up in increments like 1, 2, 3, 4 ect. When a row has the same liNumber; say 1 then that is classed as a duplicate. I then need to update that duplicate row to continue the increments, from 6, 7 , 8 ect.
Does this make sense?

Since the liNumber is not of identical sequence like (1,2,3,4...and so on) you can go with a workaround by updating the duplicates by adding the max of the liNumber with the corresponding rownumber as below.
declare #tbl table(id int, wCode int, aCode varchar(50), liNumber int)
insert into #tbl
values(7938636,1,'MZ690577',1)
,(7938636,1,'MZ690577',1)
,(7938636,1,'MZ690577',2)
,(7938636,1,'MZ690577',3)
,(7938636,1,'MZ690577',8)
,(7938636,1,'MZ690577',9)
,(7938636,1,'MZ690577',9)
declare #maxvalue int = (select max(linumber) from #tbl)
;with cte as
(
select *,ROW_NUMBER()over(partition by liNumber order by id,liNumber) partitionedrn
,#maxvalue + ROW_NUMBER()over(order by id,liNumber)maxx
from #tbl
)
update cte set liNumber = maxx
where partitionedrn > 1
select * from #tbl
Note: This is just a sample data and I did not consider your table to its entirety.

Related

SQL Server Subtract a value from records in the order of first to last

I have a table temp with 2 columns
create table temp
(
id int identity(1,1),
amount decimal(18,2)
)
Sample data insert as follows
insert into temp(amount)
values (100), (200), (500)
Table will look like
id amount
-----------
1 100
2 200
3 500
What I am trying to achieve is if suppose we deduct an amount of 150 from the table then deduction should happen in the order of Id. Which means the amount of id 1 will be 0 (100-150 =0 and remaining is 50) and then amount of id 2 will be 150 (balance 50 from previous deduction must be reduced from 200)
So the result data set should be
id amount
---------
1 0
2 150
3 500
Window cumulative summation for all previous records, save to cte for aliasing:
create table #a(id int,amount int)
insert #a values(1,100),(2,200),(3,500)
select * from #a
declare #sub int=150
;with cte as
(
select id,amount,isnull(sum(amount) over (order by id rows between unbounded preceding and 1 preceding),0) as prev_sum
from #a
)
select *,case
when #sub-prev_sum>amount then 0
when #sub-prev_sum>0 then amount-(#sub-prev_sum)
else amount
end
from cte

SQL SERVER Replace Null

I have a tsql query like that:
DECLARE #TABLE TABLE(
id int primary key identity(1,1),
code int );
INSERT INTO #TABLE VALUES (1),(NULL),(NULL),(NULL),(2),(NULL),(NULL),(NULL),(3),(NULL),(NULL),(NULL);
SELECT
id ,
code
FROM #TABLE T1
My result :
id code
1 1
2 NULL
3 NULL
4 NULL
5 2
6 NULL
7 NULL
8 NULL
9 3
10 NULL
11 NULL
12 NULL
I want to change null value with null value's last one numeric value. I want to see that:
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 2
9 3
10 3
11 3
12 3
But i dont want to use while loop. How can i do it ?
If the values are increasing, use a cumulative max:
select t.*,
max(code) over (order by id) as imputed_code
from #table t;
If the code is not strictly increasing, then you can do this in two steps:
select t.*, max(code) over (order by grp) as imputed_code
from (select t.*,
count(code) over (order by id) as grp
from #table t
) t;
Given that this is a table variable, I'm guessing that you don't really want to update it. But if you do:
with toupdate as (
<one of the above queries>
)
update toupdate
set code = imputed_code
where code is null;

SQL Data Grouping

I was hoping someone would be able to help me (or point me in the right direction) on the following problem. I'm looking at grouping a large number of codes to only 3 digits, ensuring that if a participant had the code 122.2 and 122.3, it would count as one occurrence and not two.
Data Example:
Participant | group_code
1 | 1223
1 | 1224
1 | 1123
2 | 1012
2 | 0123
Current Code:
SELECT (left(group_code, 3)) as Group, count(left(group_code, 3)) as occurrence
from testDB
group by left(group_code, 3)
I suspect I need to use a unique element on the participant ID when grouping, however I'm not too sure.
Current Outcome:
Using the current data example, the result is as follows.
122 has 2 occurrences
112 has 1 occurrence
101 has 1 occurrence
012 has 1 occurrence
Expected Outcome:
122 has 1 occurrences
112 has 1 occurrence
101 has 1 occurrence
012 has 1 occurrence
Question: Is it possible to change the current code so that, if a single participant has multiple occurrences of a 3 digit value, for example 111.1, 111.2, 111.3, and 111.4, using the code above would provide the out 111 has occurred 4 times. However, I only want it to state it has appeared once as I'm only interested in a 3 digit level (and not the 4th).
Many thanks
Try this.
declare #t table ( group_code varchar(15))
insert into #t values ('122.2') ,('122.3' ) ,( '122.4' ) ,( '112.6'),( '112.0') , ( '119.1')
SELECT (left(group_code, 3)) as Grop,
count(left(group_code, 3)) as occurrence
from #t
group by left(group_code, 3)
select * from
(
SELECT (left(group_code, 3)) as Grop,
count(left(group_code, 3)) as occurrence
from #t
group by left(group_code, 3)
) a
join #t t on a.Grop = left(t.group_code, 3)
Create Table #T(Id int, Value decimal(16,2))
Insert into #T
Values(1,122.2),(1,122.3),(2,122.2)
Select Id,ROUND(Value,0)
from #T
Group By Id,ROUND(Value,0)
Try this
SELECT (left(group_code, 3)) as Group, count(*) as occurrence
from testDB
group by left(group_code, 3)
EDIT:
According to your last edit in your question it should be this:
DECLARE #tbl TABLE(Participant INT, group_code INT);
INSERT INTO #tbl VALUES
(1,1223)
,(1,1224)
,(1,1123)
,(2,1012)
,(3,0123);
WITH WithGroupingNew AS
(
SELECT tbl.*
,LEFT(CAST(tbl.group_code AS varchar(100)),3) AS NewGroupingCode
FROM #tbl AS tbl
)
,Counted AS
(
SELECT *
,ROW_NUMBER() OVER(PARTITION BY NewGroupingCode ORDER BY group_code) AS Counter
FROM WithGroupingNew
)
SELECT *
FROM Counted
WHERE Counter=1
This as before the edit, might be useful though...
With this CTE you get a field with the information you need:
DECLARE #tbl TABLE(Participant INT, group_code INT);
INSERT INTO #tbl VALUES
(1,1223)
,(1,1224)
,(1,1123)
,(2,1012)
,(3,0123);
WITH WithGroupingNew AS
(
SELECT tbl.*
,LEFT(CAST(tbl.group_code AS varchar(100)),3) AS NewGroupingCode
FROM #tbl AS tbl
)
SELECT * FROM WithGroupingNew
Result
Participant group_code NewGroupingCode
1 1223 122
1 1224 122
1 1123 112
2 1012 101
3 0123 123
It's on you to decide, what you are going to do with this:
SELECT DISTINCT NewGroupingCode FROM WithGroupingNew
or maybe
SELECT DISTINCT Participant,NewGroupingCode FROM WithGroupingNew
or maybe
SELECT *,ROW_NUMBER() OVER(PARTITION BY NewGroupingCode ORDER BY group_code)

The row must be shown same order in result set according to column value

I am using SQL Server 2008 R2.
I have the following data:
ID Value OrderNumber
1 A NULL
2 E 4
3 C NULL
4 B NULL
5 F 2
6 D NULL
i want to write a query that must fetch data ordering by OrderNumber column considering OrderNumber values. The query result must be below:
ID Value OrderNumber
1 A NULL
5 F 2 --indicates row must be second in result set.
3 C NULL
2 E 4 --indicates row must be fourth in result set.
4 B NULL
6 D NULL
Thanks for reading and your answers.
I've tried a number of different ways, but the only way I can find that produces the required results in a guaranteed way is:
declare #t table (ID int not null,Value char(1) not null,OrderNumber int null)
insert into #T(ID,Value,OrderNumber) values
(1,'A',NULL),
(2,'E',4),
(3,'C',NULL),
(4,'B',NULL),
(5,'F',2),
(6,'D',NULL)
;With Nbrs as (
select ROW_NUMBER() OVER (ORDER BY ID) as n from #t
), AvailableNbrs as (
select n,ROW_NUMBER() OVER (ORDER BY n) as rn from Nbrs where n not in (select OrderNumber from #t where OrderNumber is not null)
), RequiredOrders as (
select ID,ROW_NUMBER() OVER (ORDER BY ID) as rn from #t where OrderNumber is null
)
select
*,COALESCE(OrderNumber,an.n) as FinalOrder
from
#t t
left join
RequiredOrders ro
on
t.ID = ro.ID
left join
AvailableNbrs an
on
ro.rn = an.rn
order by COALESCE(OrderNumber,an.n)
Where we use a few CTEs to find OrderNumbers that aren't currently assigned, and to match those 1-1 with rows which have no OrderNumber.
Results:
|--------- #t --------------| |----- RequiredOrders ---------| |----- AvailableNbrs -------------------| |- COALESCE -------|
ID Value OrderNumber ID rn n rn FinalOrder
----------- ----- ----------- ----------- -------------------- -------------------- -------------------- --------------------
1 A NULL 1 1 1 1 1
5 F 2 NULL NULL NULL NULL 2
3 C NULL 3 2 3 2 3
2 E 4 NULL NULL NULL NULL 4
4 B NULL 4 3 5 3 5
6 D NULL 6 4 6 4 6
Found a much better solution than the accepted answer:
declare #t table(id int, value char, ordernumber int)
insert #t values(1,'A', null)
insert #t values(2,'E',4)
insert #t values(3,'C',NULL)
insert #t values(4,'B',NULL)
insert #t values(5,'F',2)
insert #t values(6,'D',NULL)
;with a as
(
select *, row_number() over (order by id)+.1 rn1 from #t
where ordernumber is null
union all
select *, ordernumber - rank() over (order by ordernumber)+1 rn1 from #t
where ordernumber is not null
)
select * from a order by rn1, ordernumber
Try this:
SELECT *
FROM tableName
Order BY CASE WHEN OrderNumber IS NULL THEN ID ELSE OrderNumber END

Create indexed view

My table structure is below :
MyTable (ID Int, AccID1 Int, AccID2 Int, AccID3 int)
ID AccID1 AccID2 AccID3
---- -------- -------- --------
1 12 2 NULL
2 4 12 1
3 NULL NULL 5
4 7 NULL 1
I want to create indexed view with below output :
ID Level Value
---- ----- -------
1 1 12
1 2 2
2 1 4
2 2 12
2 3 1
3 3 5
4 1 7
4 3 1
EDIT :
My table is very huge and I want to have above output.
I can Get my query such as below :
Select ID,
Case StrLevel
When 'AccID1' Then 1
When 'AccID2' Then 2
Else 3
End AS [Level],
AccID as Value
From (
Select A.ID, A.AccID1, A.AccID2, A.AccID3
From MyTable A
)as p
UNPIVOT (AccID FOR [StrLevel] IN (AccID1, AccID2, AccID3)) AS unpvt
or
Select *
from (
select MyTable.ID,
num.n as [Level],
Case Num.n
When 1 Then MyTable.AccID1
When 2 Then MyTable.AccID2
Else MyTable.AccID3
End AS AccID
from myTable
cross join (select 1
union select 2
union select 3)Num(n)
)Z
Where Z.AccID IS NOT NULL
or
Select A.ID,
2 AS [Level],
A.AccID1 AS AccID
From MyTable A
Where A.AccID1 IS NOT NULL
Union
Select A.ID,
2 AS [Level],
A.AccID2
From MyTable A
Where A.AccID2 IS NOT NULL
Union
Select A.ID,
3 AS [Level],
A.AccID3
From MyTable A
Where A.AccID3 IS NOT NULL
But Above query is slow and I want to have indexed view to have better performance.
and in indexed view I can't use UNION or UNPIVOT or CROSS JOIN in indexed view.
What if you created a Numbers table to essentially do the work of your illegal CROSS JOIN?
Create Table Numbers (number INT NOT NULL PRIMARY KEY)
Go
Insert Numbers
Select top 30000 row_number() over (order by (select 1)) as rn
from sys.all_objects s1 cross join sys.all_objects s2
go
Create view v_unpivot with schemabinding
as
Select MyTable.ID,
n.number as [Level],
Case n.number
When 1 Then MyTable.AccID1
When 2 Then MyTable.AccID2
Else MyTable.AccID3
End AS AccID
From dbo.Mytable
Join dbo.Numbers n on n.number BETWEEN 1 AND 3
go
Create unique clustered index pk_v_unpivot on v_unpivot (ID, [Level])
go
Select
ID,
[Level],
AccID
From v_unpivot with (noexpand)
Where AccID IS NOT NULL
Order by ID, [Level]
The WHERE AccID IS NOT NULL must be part of the query because derived tables are not allowed in indexed views.