How to update multiple rows in SQL Server? - sql-server

Here is the Insert into select query which selects multiple rows from GradePackages table and inserts the data into EmployeePackages table:
insert into EmployeePackages (EmployeeId, GradeId, PackageId, Amount)
select
#EmployeeId,
#GradeId,
PackageId,
Amount
from
GradePackages
where
GradeId = #GradeId
Here #EmployeeId and GradeId have a single value each. But PackageId and Amount, each of them have 5 values.
The Insert query works fine, but the problem is with the update query. I need to update i.e. copy rows from the GradePackages table (where I input a specific GradeId) into the EmployeePackages table (where specific EmployeeId is input). I know it will work on a single row but there are multiple rows and that is the problem. I have tried different types of Update queries but it doesn't work. Have a look please. Thank you.
EmployeePackages table:
Id
EmployeeId
GradeId
PackageId
Amount
13
1036
30
1
29980.00
14
1036
30
2
5000.00
15
1036
30
3
0.00
16
1036
30
4
0.00
17
1036
30
5
0.00
18
1037
31
1
34000.00
19
1037
31
2
6000.00
20
1037
31
3
0.00
21
1037
31
4
0.00
22
1037
31
5
0.00
GradePackages table:
Id
GradeId
PackageId
Amount
11
30
1
34650.00
12
30
2
5000.00
13
30
3
0.00
14
30
4
0.00
15
30
5
0.00
16
29
1
41090.00
17
29
2
6000.00
18
29
3
0.00
19
29
4
0.00
20
29
5
0.00
Output needed (EmployeePackages):
Id
EmployeeId
GradeId
PackageId
Amount
13
1036
29
1
41090.00
14
1036
29
2
6000.00
15
1036
29
3
0.00
16
1036
29
4
0.00
17
1036
29
5
0.00
18
1037
31
1
34000.00
19
1037
31
2
6000.00
20
1037
31
3
0.00
21
1037
31
4
0.00
22
1037
31
5
0.00
Expected results:
Let's say I select rows with GradeId = 29 (5 rows) from GradePackages and update EmployeePackages where EmployeeId = 1036

I believe you said Update; not insert so...
DECLARE #GradeID AS Numeric(4,0)=29
DECLARE #EmployeeID as Numeric(4,0)=1036
UPDATE EmployeePackages
SET EmployeePackages.Amount = GP.Amount,
EmployeePackages.GradeID = #GradeID --added this and , above.
FROM EmployeePackages EP
INNER JOIN GradePackages GP
ON EP.PackageID = GP.PackageID
WHERE EP.EmployeeID = #EmployeeID
AND GP.GradeID = #GradeID
Consider:
Parameterizing SQL UPDATE query
SQL Update from One Table to Another Based on a ID Match
Example Fiddle: Special thanks to RF1991 so I didn't have to re-create the fiddle.
Resulting in:
+---------+------------+---------+-----------+----------+
| Id | EmployeeId | GradeId | PackageId | Amount |
+---------+------------+---------+-----------+----------+
| 13 | 1036 | 29 | 1 | 41090.00 |
| 14 | 1036 | 29 | 2 | 6000.00 |
| 15 | 1036 | 29 | 3 | 0.00 |
| 16 | 1036 | 29 | 4 | 0.00 |
| 17 | 1036 | 29 | 5 | 0.00 |
| 18 | 1037 | 31 | 1 | 34000.00 |
| 19 | 1037 | 31 | 2 | 6000.00 |
| 20 | 1037 | 31 | 3 | 0.00 |
| 21 | 1037 | 31 | 4 | 0.00 |
| 22 | 1037 | 31 | 5 | 0.00 |
+---------+------------+---------+-----------+----------+

for update using Subquery
UPDATE employeepackages
SET employeepackages.gradeid = t.gradeid,
employeepackages.amount = t.amount
FROM (SELECT GP.gradeid,
GP.packageid,
GP.amount
FROM gradepackages GP
FULL JOIN employeepackages EP
ON EP.gradeid = GP.gradeid
AND EP.packageid = GP.packageid
WHERE EP.gradeid IS NULL) t
WHERE employeeid = #EmployeeId
AND employeepackages.gradeid = #GradeId
AND employeepackages.packageid = t.packageid
subquery fiddle
or with CTE
;with t as(
SELECT
GP.gradeid ,
GP.packageid ,
GP.amount
FROM gradepackages GP
FULL JOIN employeepackages EP
ON EP.gradeid = GP.gradeid
AND EP.packageid = GP.packageid
WHERE EP.gradeid IS NULL )
UPDATE e
SET e.gradeid = t.gradeid,
e.amount = t.amount
FROM employeepackages e
JOIN t
ON e.packageid = t.packageid
WHERE e.employeeid = #EmployeeId
AND e.GradeId = #GradeId
cte fiddle

Related

Calculate row difference within groups

I'm looking for help with calculating the difference between consecutive ordered rows within groups in SQL (Microsoft SQL server).
I have a table like this:
ID School_ID Enrollment_Start_Date Order
1 56 1/1/2018 10
1 56 5/5/2018 24
1 56 7/7/2018 35
1 103 4/4/2019 26
1 103 3/3/2019 19
I want to calculate the difference between Order, group by ID, School_ID, and order by Enrollment_Start_Date.
so I want something like this:
ID School_ID Enrollment_Start_Date Order Diff
1 56 1/1/2018 10 10 # nothing to be subtracted from 10
1 56 5/5/2018 24 14 # 24-10
1 56 7/7/2018 35 11 # 35-24
1 103 3/3/2019 19 19 # nothing to be subtracted from 19
1 103 4/4/2019 26 7 # 26-19
I have hundreds of IDs, and each ID can have at most 6 Enrollment_Start_Date, so I'm looking for some generalizable implementations.
Use LAG(<column>) analytic function to obtain a "previous" column value specified within the OVER part, then substract current value from it and make it a positive number multiplying it by -1. If previous value isn't present (is null) then take the current value.
Pseudo code would be:
If previous_order_value exists:
-1 * (previous_order_value - current_order_value)
Else
current_order_value
where previous_order_value is based on the same id & school_id and is sorted by enrollment_start_date in ascending order
SQL Code:
select
id,
school_id,
enrollment_start_date,
[order],
coalesce(-1 * (lag([order]) over (partition by id, school_id order by enrollment_start_date ) - [order]), [order]) as diff
from yourtable
Also note, that order keyword is reserved in SQL Server, which is why your column was created with name wrapped within [ ]. I suggest using some other word for this column, if possible.
use lag() analytic function for getting difference of two row and case when for getting orginal value of order column where no difference exist
with cte as
(
select 1 as id, 56 as sclid, '2018-01-01' as s_date, 10 as orders
union all
select 1,56,'2018-05-05',24 union all
select 1,56,'2018-07-07',35 union all
select 1,103,'2019-04-04',26 union all
select 1,103,'2019-03-03',19
) select t.*,
case when ( lag([orders])over(partition by id,sclid order by s_date ) -[orders] )
is null then [orders] else
( lag([orders])over(partition by id,sclid order by s_date ) -[orders] )*(-1) end
as diff
from cte t
output
id sclid s_date orders diff
1 56 2018-01-01 10 10
1 56 2018-05-05 24 14
1 56 2018-07-07 35 11
1 103 2019-03-03 19 19
1 103 2019-04-04 26 7
demo link
Use LAG(COLUMN_NAME)
Query
SELECT id, School_ID, Enrollment_Start_Date, cOrder,
ISNULL((cOrder - (LAG(cOrder) OVER(PARTITION BY id, School_ID ORDER BY Enrollment_Start_Date))),cOrder)Diff
FROM Table1
Samle Output
| id | School_ID | Enrollment_Start_Date | cOrder | Diff |
|----|-----------|-----------------------|--------|------|
| 1 | 56 | 2018-01-01 | 10 | 10 |
| 1 | 56 | 2018-05-05 | 24 | 14 |
| 1 | 56 | 2018-07-07 | 35 | 11 |
| 1 | 103 | 2019-03-03 | 19 | 19 |
| 1 | 103 | 2019-04-04 | 26 | 7 |
SQL Fiddle Demo

SQL server: How to count maximum consercutive change for each observation in

I'd used sql server 2012. I want to count maximum consecutive change for each obs. The table like this
snapshot_date customer_id Number Max_consercutive_increase_as_of_each_row
Jan-14 12342 0 0
Feb-14 12342 15 1
Mar-14 12342 45 2
Apr-14 12342 0 2
May-14 12342 15 2
Jun-14 12342 45 2
Jul-14 12342 75 3
Aug-14 12342 105 4
Sep-14 12342 135 5
Oct-14 12342 0 4
Nov-14 12342 0 3
Dec-14 12342 0 2
Jan-15 12342 0 1
Feb-15 12342 0 0
Mar-15 12342 0 0
Apr-15 12342 0 0
As of each rows, count backward to 06 preceding rows (include current row). Of course, some starting rows only 01 or 02 rows before. Increasing based on 'number' column. In 06 rows, if max consecutive between 2 and 3 --> i want take 3.
I try to use cursor with fetch relative -n rows, but my code is not working.
So please help me to resolve it.
Thanks so much!
This should work for you:
-- Create test data
declare #t table(snapshot_date date,customer_id int,Number int);
insert into #t values ('20140101',12342, 0 ),('20140201',12342, 15 ),('20140301',12342, 45 ),('20140401',12342, 0 ),('20140501',12342, 15 ),('20140601',12342, 45 ),('20140701',12342, 75 ),('20140801',12342, 105 ),('20140901',12342, 135 ),('20141001',12342, 0 ),('20141101',12342, 0 ),('20141201',12342, 0 ),('20150101',12342, 0 ),('20150201',12342, 0 ),('20150301',12342, 0 ),('20150401',12342, 0 );
with d as -- Add a row number to the dataset
(
select snapshot_date
,customer_id
,Number
,row_number() over (order by snapshot_date) as rn
from #t
)
,c as -- Use a recursive CTE to loop through the dataset and check for increases
(
select snapshot_date
,customer_id
,Number
,rn
,0 as ConsecutiveIncreases
from d
where rn = 1
union all
select t.snapshot_date
,t.customer_id
,t.Number
,t.rn
,case when t.Number > c.Number then c.ConsecutiveIncreases + 1 else 0 end
from d as t
join c
on t.rn = c.rn+1
)
-- Take the MAX consecutive increase where the current row is also an increase,
-- unless the row is not an increase, then subtract the number of non-increases
-- from the MAX consecutive increase to find the number of increases within the last 6 rows.
-- If less than 6 rows to use, just take the MAX increase.
select c.snapshot_date
,c.customer_id
,c.Number
,case when isnull(sum(c2.ConsecutiveIncreases),0) = 0
then 0
when count(c2.ConsecutiveIncreases) < 6
then max(c2.ConsecutiveIncreases)
else max(c2.ConsecutiveIncreases) - case when c.ConsecutiveIncreases = 0
then sum(case when c2.ConsecutiveIncreases = 0
then 1
else 0
end
)
else 0
end
end as MaxConsecutiveIncreases
from c
left join c as c2
on c2.rn between c.rn-5 and c.rn
group by c.snapshot_date
,c.customer_id
,c.Number
,c.ConsecutiveIncreases
order by 1
Output:
+---------------+-------------+--------+-------------------------+
| snapshot_date | customer_id | Number | MaxConsecutiveIncreases |
+---------------+-------------+--------+-------------------------+
| 2014-01-01 | 12342 | 0 | 0 |
| 2014-02-01 | 12342 | 15 | 1 |
| 2014-03-01 | 12342 | 45 | 2 |
| 2014-04-01 | 12342 | 0 | 2 |
| 2014-05-01 | 12342 | 15 | 2 |
| 2014-06-01 | 12342 | 45 | 2 |
| 2014-07-01 | 12342 | 75 | 3 |
| 2014-08-01 | 12342 | 105 | 4 |
| 2014-09-01 | 12342 | 135 | 5 |
| 2014-10-01 | 12342 | 0 | 4 |
| 2014-11-01 | 12342 | 0 | 3 |
| 2014-12-01 | 12342 | 0 | 2 |
| 2015-01-01 | 12342 | 0 | 1 |
| 2015-02-01 | 12342 | 0 | 0 |
| 2015-03-01 | 12342 | 0 | 0 |
| 2015-04-01 | 12342 | 0 | 0 |
+---------------+-------------+--------+-------------------------+

How to increase the month by one in a date based on condition in SQL Server

I am trying to to extend the valid_till date for a month of tenants who have reference id more than two times.
refid, referrer_id, referrer_bonus_amount, referral_valid, valid_from, valid_till
1 2 2500 1 2015-07-05 2015-09-05
2 3 2500 1 2015-07-05 2015-09-05
3 5 1000 0 2015-12-13 2016-02-13
4 6 2500 0 2016-04-25 2016-06-24
5 10 1000 1 2015-07-01 2015-09-01
6 12 2500 1 2015-05-12 2015-07-12
7 13 2500 0 2015-08-05 2015-10-05
8 20 1000 1 2016-02-05 2016-04-05
9 2 2500 0 2015-08-12 2015-09-12
10 5 91000 1 2016-02-18 2016-04-18
11 20 1500 1 2016-06-19 2016-08-19
12 9 2500 0 2015-11-15 2016-01-15
13 13 91000 1 2016-02-01 2016-04-01
14 5 1000 1 2016-04-25 2016-06-24
To update the table (t) to add 1 month to the valid_till date for those refid that appear in referrer_id more than two times using exists() with having count(*) > 2:
update t
set valid_till = dateadd(month,1,valid_till)
output inserted.*
from t
where exists (
select 1
from t as i
where i.referrer_id = t.refid
group by referrer_id
having count(*) > 2
)
rextester demo: http://rextester.com/WXZC31875
output:
+-------+-------------+-----------------------+----------------+------------+------------+
| refid | referrer_id | referrer_bonus_amount | referral_valid | valid_from | valid_till |
+-------+-------------+-----------------------+----------------+------------+------------+
| 5 | 10 | 1000 | 1 | 2015-07-01 | 2015-10-01 |
+-------+-------------+-----------------------+----------------+------------+------------+

TSQL Divide set in groups

I have a table with 3 fields: wk, cor, id
"wk" is the week, "cor" groups items from same location, "id" is the id of each item to retrieve from warehouse.
Given a certain number of items to retrieve, I must take almost the same quantity of items from each group ("cor" represents groups) for balancing the warehouse performance, respecting the week precedence (before going to the following week, the previous must be ehausted).
If you follow the link the image may be clear:
Data sample
rows are taken in this order:
yellow, orange, green, gray (this last one starts with "cor 2" because "cor 1" was the last used in week 28)
The RES column (done by hand in the sample) represents the right order I should take items; currently this is obtained with a cursor, which is very very slow and I'd like to do something better, if possible; I've tried with windowed functions, cte, recursive cte but was not able to get anything right.
With this script you can have the same table
DECLARE #t TABLE (wk int, cor int, id int)
INSERT INTO #t
(
wk
,cor
,id
)
VALUES
(28,1,4044534),
(28,1,6778322),
(28,1,7921336),
(28,1,4326390),
(28,2,2669622),
(28,2,6580257),
(28,2,1179795),
(28,3,3980111),
(28,3,2549129),
(28,3,6763533),
(29,1,6023538),
(29,1,8219574),
(29,1,3836858),
(29,2,3355314),
(29,2,148847),
(29,2,8083320),
(29,3,1359966),
(29,3,8746308)
The expected result:
All fields are given while the RES field must be calculated and represents the order in which items will be taken out (explained below the table).
+----+-----+---------+-----+
| wk | cor | id | RES |
+----+-----+---------+-----+
| 28 | 1 | 4044534 | 1 |
| 28 | 1 | 6778322 | 4 |
| 28 | 1 | 7921336 | 7 |
| 28 | 1 | 4326390 | 10 |
| 28 | 2 | 2669622 | 2 |
| 28 | 2 | 6580257 | 5 |
| 28 | 2 | 1179795 | 8 |
| 28 | 3 | 3980111 | 3 |
| 28 | 3 | 2549129 | 6 |
| 28 | 3 | 6763533 | 9 |
| 29 | 1 | 6023538 | 11 |
| 29 | 1 | 8219574 | 14 |
| 29 | 1 | 3836858 | 17 |
| 29 | 2 | 3355314 | 12 |
| 29 | 2 | 148847 | 15 |
| 29 | 2 | 8083320 | 18 |
| 29 | 3 | 1359966 | 13 |
| 29 | 3 | 8746308 | 16 |
+----+-----+---------+-----+
The algo is like that:
The older week must be first exausted (in the sample, wk 28 must be finished before taking itmes from wk 29)
Items must be equally reparted in "cor"s, so if 10 items are required they must come out like that: 3 from cor1,3 from cor2, 3 from cor3. The last one may come from whichever cor because 10 is not divisible by 3, obv
If 11 items are required; week 28 only contains 10 items so the last one will be taken from week 29, with the same principle: equally distribute the exit among cors, even if weeks change. If the last article from week 28 was taken from cor 1, the next one in week 29 will be taken from cor 2
Does this answer your problem ?
DROP TABLE IF EXISTS #temp
DROP TABLE IF EXISTS #temp2
CREATE TABLE #temp (idx INT PRIMARY KEY IDENTITY(1,1), wk int, cor int, id int)
INSERT INTO #temp
VALUES
(28,1,4044534),
(28,1,6778322),
(28,1,7921336),
(28,1,4326390),
(28,2,2669622),
(28,2,6580257),
(28,2,1179795),
(28,3,3980111),
(28,3,2549129),
(28,3,6763533),
(29,1,6023538),
(29,1,8219574),
(29,1,3836858),
(29,2,3355314),
(29,2,148847),
(29,2,8083320),
(29,3,1359966),
(29,3,8746308)
SELECT wk, cor, id
, ROW_NUMBER() OVER (ORDER BY wk, RES, idx) as RES
FROM (
SELECT idx
, wk
, cor
, id
, ROW_NUMBER() OVER (PARTITION BY wk, cor ORDER BY cor) AS RES
FROM #temp
) AS t
ORDER BY idx
You don't have the correct information in your test data to support the desired output.
If however, you were to have an identity column that represents the insertion order, you could use something like the following...
WITH cte_RankOrder AS (
SELECT
t.rn, t.wk, t.cor, t.id,
RankOrder = DENSE_RANK() OVER (PARTITION BY t.wk, t.cor ORDER BY t.rn, t.wk)
FROM
#t t
)
SELECT
ro.rn, ro.wk, ro.cor, ro.id,
RES = ROW_NUMBER() OVER (ORDER BY wk, ro.RankOrder, ro.cor)
FROM
cte_RankOrder ro
ORDER BY ro.rn;
results...
rn wk cor id RES
----------- ----------- ----------- ----------- --------------------
1 28 1 4044534 1
2 28 1 6778322 4
3 28 1 7921336 7
4 28 1 4326390 10
5 28 2 2669622 2
6 28 2 6580257 5
7 28 2 1179795 8
8 28 3 3980111 3
9 28 3 2549129 6
10 28 3 6763533 9
11 29 1 6023538 11
12 29 1 8219574 14
13 29 1 3836858 17
14 29 2 3355314 12
15 29 2 148847 15
16 29 2 8083320 18
17 29 3 1359966 13
18 29 3 8746308 16
HTH, Jason

How to group by same data with connected records in SQL Server

I have a table output like....
srno billno particular Qty rate vat amount paid balance
1 25 aaa 5 20 5 105 400 135
2 25 qqq 5 50 5 225 400 135
3 25 fff 10 20 5 205 400 135
4 26 aaa 10 20 5 205 300 245
5 26 fff 10 20 5 205 300 245
but I want output like:
srno billno particular Qty rate vat amount paid balance
1 25 aaa 5 20 5 105 400 135
qqq 5 50 5 225
fff 10 20 5 205
2 26 aaa 10 20 5 205 300 245
fff 10 20 5 205
How to get this output with a T-SQL select command....
You should be able to do this by using the ranking functions row_number() and dense_rank:
select
case when rn = 1 then cast(rnk as varchar(10)) else '' end srno,
case when rn = 1 then cast(billno as varchar(10)) else '' end billno,
[particular],
[Qty],
[rate],
[vat],
[amount],
case when rn = 1 then cast([paid] as varchar(10)) else '' end [paid],
case when rn = 1 then cast([balance] as varchar(10)) else '' end [balance]
from
(
select [srno], [billno], [particular], [Qty], [rate], [vat], [amount], [paid], [balance],
row_number() over(partition by billno order by srno) rn,
dense_rank() over(order by billno) rnk
from yourtable
) src;
See SQL Fiddle with Demo
The result is:
| SRNO | BILLNO | PARTICULAR | QTY | RATE | VAT | AMOUNT | PAID | BALANCE |
---------------------------------------------------------------------------
| 1 | 25 | aaa | 5 | 20 | 5 | 105 | 400 | 135 |
| | | qqq | 5 | 50 | 5 | 225 | | |
| | | fff | 10 | 20 | 5 | 205 | | |
| 2 | 26 | aaa | 10 | 20 | 5 | 205 | 300 | 245 |
| | | fff | 10 | 20 | 5 | 205 | | |

Resources