Update table based on identical values in a column on same table - sql-server

I have a table like this
InsuredID | EmployeeNumber | MemberTypeID | LinkedMemberID
----------------------------------------------------------------
1001012 | 39018 | 102 | 0
1001061 | 39018 | 100 | 0
1001147 | 39019 | 102 | 0
1001196 | 39019 | 100 | 0
I need to update LinkedMemberID in this table to value of InsuredID of the with same EmployeeNumber and MemberTypeID = 100 for all MemberTypes which are not 100.
LinkedMemberID of MemberTypeID = 100 will remain 0.
After update, the table should look like
InsuredID | EmployeeNumber | MemberTypeID | LinkedMemberID
----------------------------------------------------------------
1001012 | 39018 | 102 | 1001061
1001061 | 39018 | 100 | 0
1001147 | 39019 | 102 | 1001196
1001196 | 39019 | 100 | 0
I have tried various SQLupdate statements but can't figure out how to do this. I am using SQL Server 2008. Please help.

If I understand correctly, you want an update statement and you can do what you want with a join:
update toupdate
set LinkedMemberID = t.InsuredID
from table toupdate join
table t
on toupdate.EmployeeNumber = t.EmployeeNumber and
t.MemberTypeID = 100
where toupdate.MemberTypeID <> 100;

You can use the APPLY function:
SELECT
t.InsuredID,
EmployeeNumber,
MemberTypeID,
LinkedMemberID = CASE WHEN t.MemberTypeID = 100 THEN LinkedMemberID ELSE x.InsuredID END
FROM tbl t
OUTER APPLY (
SELECT InsuredID
FROM tbl
WHERE EmployeeNumber = t.EmployeeNumber
AND MemberTypeID = 100
)x
SQL Fiddle
Transforming it to an UPDATE statement:
UPDATE t
SET t.LinkedMemberID = x.InsuredID
FROM tbl t
OUTER APPLY (
SELECT TOP 1 InsuredID
FROM tbl
WHERE EmployeeNumber = t.EmployeeNumber
AND MemberTypeID = 100
ORDER BY InsuredID DESC
)x
WHERE t.MemberTypeID <> 100
SQL Fiddle

You can try this:
UPDATE Your_Table
SET LinkedMemberID = (SELECT TOP 1 T.InsuredID
FROM Your_Table T
WHERE T.EmployeeNumber = Your_Table.EmployeeNumber
AND T.MemberTypeID = 100)
WHERE MemberTypeID <> 100

Related

SQL Server UPDATE - GROUP BY - MAX

This is SQL Server 2016. I have the following data in only one table:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+--------+-------------+-----------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 0
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 0
I want to group by custID, prodID and title and update isMasterTitle field to 1 for every max() titleCount per group.
So, I want the following:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+----------+------------+---------------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 1
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 1
I'm trying the following:
UPDATE [dbo].[_Variations]
SET isMasterTitle = 1
FROM [dbo].[_Variations] v1
INNER JOIN (SELECT custID, prodID, MAX(titleCount) AS mtitleCount
FROM [_Variations]
GROUP BY custID,prodID) as v2 ON v1.custID = v2.custID and v1.prodID = v2.prodID and v1.titleCount = v2.mtitleCount
try the following:
;with cte
as
(
select isMasterTitle, ROW_NUMBER() over (partition by custID, prodID order by titleCount desc) rn
from #t
)
update cte
set isMasterTitle = 1
where rn = 1
select * from #t
Your given code also works fine.
Please find the db<>fiddle here.
I would recommend leveraging a powerful feature of SQL Server called the updateable common-table-expression.
You can build a cte that uses window functions to identify which row should be updated, and then directly update it; there is no need to join again the original table in the outer query. This makes the query both shorter and more efficient:
with cte as (
select
isMaster,
row_number() over(partition by custID, prodID order by titleCount desc) rn
from [dbo].[_Variations]
)
update cte set isMaster = 1 where rn = 1

Find records of nearest date SQL

I have a table dbo.X with DateTime column lastUpdated and a code product column CodeProd which may have hundreds of records, with CodeProd duplicated because the table is used as "stock history"
My Stored Procedure has parameter #Date, I want to get all CodeProd nearest to that date so for example if I have:
+----------+--------------+--------+
| CODEPROD | lastUpdated | STATUS |
+----------+--------------+--------+
| 10 | 2-1-2019 | C1 |
| 10 | 1-1-2019 | C2 |
| 10 | 31-12-2019 | C1 |
| 11 | 31-12-2018 | C1 |
| 11 | 30-12-2018 | C1 |
| 12 | 30-8-2018 | C3 |
+----------+--------------+--------+
and #Date= '1-1-2019'
I wanna get:
+----+--------------+------+
| 10 | 1-1-2019 | C2 |
| 11 | 31-12-2018 | C1 |
| 12 | 30-8-2018 | C3 |
+----+--------------+------+
How to find it?
You can use TOP(1) WITH TIES to get one row with nearest date for each CODEPROD which should be less than provided date.
Try like following code.
SELECT TOP(1) WITH TIES *
FROM [YourTableName]
WHERE lastupdated <= #date
ORDER BY Row_number()
OVER (
partition BY [CODEPROD]
ORDER BY lastupdated DESC);
You can use apply :
select distinct t.CODEPROD, t1.lastUpdated, t1.STATUS
from table t cross apply
( select top (1) t1.*
from table t1
where t1.CODEPROD = t.CODEPROD and t1.lastUpdated <= #date
order by t1.lastUpdated desc
) t1;

Using groupBy to improve my Select (select count) query

Let's say we have this and want to see all Tasks, that havent been done yet and an additional column showing how many open Tasks there are left for this customer.
I have a table like this in my database:
+------------+--------------------------+-------+
| CustomerID | Task | Done |
+------------+--------------------------+-------+
| 1 | CleanRoom | False |
| 1 | Cleandishes | True |
| 1 | WashClothes | False |
| 2 | TakeDogsOut | True |
| 2 | PlayWithKids | True |
| 3 | HaveFunWithMrSamplesWife | True |
| 3 | CleanMrSamplesCar | False |
+------------+--------------------------+-------+
I need this as returned table:
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 2 |
| 1 | WashClothes | 2 |
| 3 | CleanMrSamplesCar | 1 |
+------------+-------------------+-------------+
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I know I could go like
SELECT
a.CustomerID,
a.Task,
(
Select count(*) from myTable where
customerID = a.CustomerID and
done = False
) as DoneOverAll
FROM myTable as a
WHERE Done = False
But I think that this is very ineffective, since it would execute a Select Count for each row in my table. Is there a way to achieve this with a JOIN using groupBy or something? I'm not into GroupBy commands yet.
Okay I should have tried first. Came up with the following;
Select count(*), CustomerID from myTable group by CustomerID
All I need to do now is to get this into a join.
Okay, got it. Sorry again for not trying first!
SELECT
a.CustomerID,
a.Task,
b.cnt
FROM myTable as a
LEFT JOIN (select count(*) AS cnt, CustomerID FROM myTable GROUP BY CustomerID) as b on a.CustomerID = B.CustomerID
WHERE Done = False
Question left;
Perfect return table would be like this, but I can do that myself when I have the one above:
About this a question; Doing this will probably be a String combination task. Should I do this on the Select statement, or would it be more advisable to do that in the final application on the client computer?
+------------+-------------------+-------------+
| CustomerID | Task | DoneOverAll |
+------------+-------------------+-------------+
| 1 | CleanRoom | 1/3 |
| 1 | WashClothes | 1/3 |
| 3 | CleanMrSamplesCar | 1/2 |
+------------+-------------------+-------------+
I'm not sure why Done = False, but this is your logic. :-)
Here's what I would do, without the LEFT JOIN.
SELECT
a.CustomerID,
a.Task,
SUM(CASE WHEN a.Done = 'False' THEN 1 ELSE 0 END) DoneOverAll,
SUM(Case WHEN a.Done = 'True' THEN 1 ELSE 0 END) NotDone
FROM myTable as a
Group By a.CustomerID, a.Task
Do calculate separately .
;with tempfalse as(
SELECT
a.CustomerID,
a.Task,
count(*) as DoneOverAll
FROM myTable as a
WHERE Done = False
group by a.CustomerID, a.Task
)
, temptrue (
SELECT
a.CustomerID,
a.Task,
count(*) as total
FROM myTable as a
group by a.CustomerID, a.Task
)
SELECT
a.CustomerID,
a.Task,
cast(NULLIF(DoneOverAll,0) as varchar (10) ) + '/' + cast(NULLIF(b.total,0) as varchar (10) )
from temptrue as a left join tempfalse b
on a.CustomerID =a.CustomerID and
a.Task = b.Task

Update All other Records Based on a single record

I have a table with a million records. I need to update some columns which are null based on the existing 'not null' records of a particular id based columns. I've tried with one query, it seems to be working fine but I don't have confidence in it that it will be able to update all those 1 million records exactly the way I need. I'm providing you some sample data how my table looks like.Any help will be appreciated
SELECT * INTO #TEST FROM (
SELECT 1 AS EMP_ID,10 AS DEPT_ID,15 AS ITEM_NBR ,NULL AS AMOUNT,NULL AS ITEM_NME
UNION ALL
SELECT 1,20,16,500,'ABCD'
UNION ALL
SELECT 1,30,17,NULL,NULL
UNION ALL
SELECT 2,10,15,1000,'XYZ'
UNION ALL
SELECT 2,30,16,NULL,NULL
UNION ALL
SELECT 2,40,17,NULL,NULL
) AS A
Sample data:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | NULL | NULL |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | NULL | NULL |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | NULL | NULL |
| 2 | 40 | 17 | NULL | NULL |
+--------+---------+----------+--------+----------+
Expected result:
+--------+---------+----------+--------+----------+
| EMP_ID | DEPT_ID | ITEM_NBR | AMOUNT | ITEM_NME |
+--------+---------+----------+--------+----------+
| 1 | 10 | 15 | 500 | ABCD |
| 1 | 20 | 16 | 500 | ABCD |
| 1 | 30 | 17 | 500 | ABCD |
| 2 | 10 | 15 | 1000 | XYZ |
| 2 | 30 | 16 | 1000 | XYZ |
| 2 | 40 | 17 | 1000 | XYZ |
+--------+---------+----------+--------+----------+
I tried this but I'm unable to conclude whether it is updating all the 1 million records properly.
SELECT * FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS NOT NULL
UPDATE T SET AMOUNT=T1.AMOUNT
FROM #TEST T
inner JOIN #TEST T1 ON T1.EMP_ID=T.EMP_ID
WHERE T1.AMOUNT IS not NULL
I have used UPDATE using inner join
UPDATE T
SET T.AMOUNT = X.AMT,T.ITEM_NME=X.I_N
FROM #TEST T
JOIN
(SELECT EMP_ID,MAX(AMOUNT) AS AMT,MAX(ITEM_NME) AS I_N
FROM #TEST
GROUP BY EMP_ID) X ON X.EMP_ID = T.EMP_ID
SELECT * into #Test1
FROM #TEST
WHERE AMOUNT IS NOT NULL
For records validation run this query first
SELECT T.AMOUNT, T1.AMOUNT, T1.EMP_ID,T1.EMP_ID
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
Begin Trans
UPDATE T
SET T.AMOUNT=T1.AMOUNT, T.ITEM_NME= = T1.ITEM_NME
FROM #TEST T
inner JOIN #TEST1 T1 ON T1.EMP_ID=T.EMP_ID
WHERE T.AMOUNT IS NULL
rollback
SELECT EMP_ID,MAX(AMOUNT) as AMOUNT MAX(ITEM_NAME) as ITEM_NAME
INTO #t
FROM #TEST
GROUP BY EMP_ID
UPDATE t SET t.AMOUNT = t1.AMOUNT, t.ITEM_NAME = t1.ITEM_NAME
FROM #TEST t INNER JOIN #t t1
ON t.emp_id = t1.emp_id
WHERE t.AMOUNT IS NULL and t.ITEM_NAME IS NULL
Use MAX aggregate function to get amount and item name for each employee and then replace null values of amount and item name with those values. For validation use COUNT function to calculate the number of rows with values of amount and item name as null. If the number of rows is zero then table is updated correctly

Iterate through an SQL Server table and insert rows

A table (Table1) has the data below:
+-----------+-----------+-----------+---------+
| AccountNo | OldBranch | NewBranch | Balance |
+-----------+-----------+-----------+---------+
| 785321 | 10 | 20 | -200 |
| 785322 | 10 | 20 | 300 |
+-----------+-----------+-----------+---------+
Using the logic :
if the Balance is negative (ie. <0) then NewBranch has to be debited (Dr) and Old Branch has to be credited (Cr);
if the Balance is positive (ie. >0) then OldBranch has to be debited (Dr) and New Branch has to be credited (Cr);
rows as below have to be inserted into another Table (Table2)
+------------+------+--------+--------+
| Account NO | DrCr | Branch | Amount |
+------------+------+--------+--------+
| 785321 | Dr | 20 | 200 |
| 785321 | Cr | 10 | 200 |
| 785322 | Cr | 20 | 300 |
| 785322 | Dr | 10 | 300 |
+------------+------+--------+--------+
What are the possible solutions using a Cursor and otherwise?
Thanks,
You did not provide much in the way of details but something like this should be pretty close.
update nb
set Balance = Balance - ABS(t1.Balance)
from NewBranch nb
join Table1 t1 on t1.AccountNo = nb.AccountNo
where nb.Balance < 0
update ob
set Balance = Balance - ABS(t1.Balance)
from OldBranch ob
join Table1 t1 on t1.AccountNo = ob.AccountNo
where ob.Balance > 0
You absolutely dont need a cursor, just a set of insert statements
INSERT INTO Table2 (AccountNo,DrCr,Branch,Amount)
SELECT AccountNo,'Dr',IIF(Balance<0,NewBranch,OldBranch),IIF(balance<0,-1*balance,balance) FROM Table1
UNION ALL
SELECT AccountNo,'Cr',IIF(Balance>0,NewBranch,OldBranch),IIF(balance<0,-1*balance,balance) FROM Table1
declare #t table (Accountno int,
OldBranch INT,
NewBranch int,
Balance int)
insert into #t (Accountno,
OldBranch,
NewBranch,
Balance)
values (785321,10,20,200),
(785322,10,20,300)
select Accountno,Y.CRDR,Y.Branch,Y.Amount from #t CROSS APPLY
(Select 'Dr' AS CRDR,OldBranch AS Branch,Balance As Amount
UNION ALL
Select 'Cr',NewBranch,Balance)y

Resources