Using a Cursor to Update Rows in Single Table - sql

I am seeking guidance using MS SQL cursor or SQL while loop to merge (add, update, and set inactive) rows using single table based on criteria below resulting in seeking the final dataset:
TABLE VIEW (SELECT * FROM TABLE WHERE ENTITY = 123 (#ID)
ENTITY ENTITY_TYPE VALUE STATUS_TYPE
123 1 1 1
123 1 4 1
123 1 9 1
TABLE VIEW (SELECT * FROM TABLE WHERE ENTITY = 456 (#OverrideID)
ENTITY ENTITY_TYPE VALUE STATUS_TYPE
456 1 1 1
456 1 5 1
Final Data Set below:
ENTITY&nbsp&nbsp&nbspTYPE VALUE STATUS_TYPE
123 1 1 3
123 1 4 3
123 1 9 3
456 1 1 1
456 1 4 1
456 1 9 1
456 1 1 1
456 1 5 1
-- Check and compare each row
-- IF #OverrideID = #ID (MATCH)
----- Set #ID to inactive keeping copy
-- If #OverrideID != #ID (NOT MATCH)
----- Insert #ID data with #OverrideID (COPY)
----- Set #ID to inactive.
I began writing the following and need help. For reference, #ID = 123 and #OverrideID = 456
DECLARE #ENTITY BIGINT, #ENTITY_TYPE BIGINT, #VALUE BIGINT, #E1 BIGINT, #T1 BIGINT, #V1 BIGINT
DECLARE type_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT * FROM TypeValue WHERE ENTITY = #ID
SET NOCOUNT OFF
OPEN type_cursor
FETCH NEXT FROM type_cursor INTO #OverrideID, #ID, #ENTITY, #ENTITY_TYPE, #VALUE
WHILE (##FETCH_STATUS = 0)
BEGIN
SELECT #ENTITY, #ENTITY_TYPE, #VALUE
IF #VALUE IS NOT NULL
BEGIN
SELECT #T1 = #ENTITY_TYPE, #V1 = #VALUE
END
ELSE
BEGIN
UPDATE TypeValue
SET ENTITY = #OverrideID, ENTITY_TYPE = #T1, VALUE = #V1 WHERE ENTITY = #ID
END
FETCH NEXT FROM type_cursor INTO #OverrideID, #ID, #ENTITY, #ENTITY_TYPE, #VALUE
END
CLOSE type_cursor
DEALLOCATE type_cursor
SET NOCOUNT OFF

That final result set looks like
select entity,entity_type,value,3 status_type
from [table]
where entity = #id
union all
select #override,entity_type,value,status_type
from [table]
where entity = #id
union all
select entity,entity_type,value,status_type
from [table]
where entity = #override

Related

Use PATINDEX to extract a substring in SQL Server?

I have some specific values I want to extract out of a string in SQL Server, but I'm not sure exactly how to get it done with PATINDEX.
Take this string:
declare #Command nvarchar(500) = 'IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'
I want to extract out the values of 500000 (for #RowsPerRun), 10000 for #RowsPerBatch and the value of 7 for #NbrDaysToKeepRpt. The values will be of variable length, so I can't guarantee the #RowsPerRun value will be 6 characters.
Is that possible?
DECLARE #Command NVARCHAR(500) =
'IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'
SELECT
SearchItem = srch.Txt,
ItemIndex = st.Pos,
ItemLen = t.Ln,
Item = SUBSTRING(pfx.Txt,1,t.Ln)
FROM (VALUES('#RowsPerRun='),('#RowsPerBatch='),('#NbrDaysToKeepRpt=')) AS srch(Txt)
CROSS APPLY (VALUES(CHARINDEX(srch.Txt,#Command),LEN(srch.Txt))) AS st(Pos,Ln)
CROSS APPLY (VALUES(SUBSTRING(#Command, st.Pos+st.Ln, 500))) AS pfx(Txt)
CROSS APPLY (VALUES(PATINDEX('%[^0-9]%',pfx.Txt)-1)) AS t(Ln);
Returns:
SearchItem ItemIndex ItemLen Item
------------------ ----------- ----------- --------
#RowsPerRun= 59 6 500000
#RowsPerBatch= 79 5 10000
#NbrDaysToKeepRpt= 100 1 7
Note that I included a few extra columns to help you understand what's happening.
Update: Against a table
This is how you would apply this logic to a series of values:
DECLARE #sometable TABLE (CommandId INT IDENTITY, Command NVARCHAR(500));
INSERT #sometable (Command)
VALUES
('IF dbo.SomeFunctionFn() = 1 BEGIN EXEC SomeStoredProcPR #RowsPerRun=500000, #RowsPerBatch=10000, #NbrDaysToKeepRpt=7 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC SomeStoredProcPR #RowsPerRun=123, #RowsPerBatch=500, #NbrDaysToKeepRpt=20 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC XXX #RowsPerRun=43, #RowsPerBatch=1000, #NbrDaysToKeepRpt=120 END'),
('IF dbo.SomeFunctionFn() = 5 BEGIN EXEC abc.yyy #RowsPerRun=43, #RowsPerBatch=1000, #NbrDaysToKeepRpt=120 END');
SELECT t.CommandId, f.SearchItem, f.Item
FROM #sometable AS t
CROSS APPLY
(
SELECT
SearchItem = srch.Txt,
ItemIndex = st.Pos,
ItemLen = t.Ln,
Item = SUBSTRING(pfx.Txt,1,t.Ln)
FROM (VALUES('#RowsPerRun='),('#RowsPerBatch='),('#NbrDaysToKeepRpt=')) AS srch(Txt)
CROSS APPLY (VALUES(CHARINDEX(srch.Txt,t.Command),LEN(srch.Txt))) AS st(Pos,Ln)
CROSS APPLY (VALUES(SUBSTRING(t.Command, st.Pos+st.Ln, 500))) AS pfx(Txt)
CROSS APPLY (VALUES(PATINDEX('%[^0-9]%',pfx.Txt)-1)) AS t(Ln)
) AS f;
Returns:
CommandId SearchItem Item
----------- ------------------ --------
1 #RowsPerRun= 500000
1 #RowsPerBatch= 10000
1 #NbrDaysToKeepRpt= 7
2 #RowsPerRun= 123
2 #RowsPerBatch= 500
2 #NbrDaysToKeepRpt= 20
3 #RowsPerRun= 43
3 #RowsPerBatch= 1000
3 #NbrDaysToKeepRpt= 120
4 #RowsPerRun= 43
4 #RowsPerBatch= 1000
4 #NbrDaysToKeepRpt= 120

How to get parents up to 5 level hierarchy from child id

I created a table with name "content_folder" and inserted values just like this.
cf_id cf_parent_id cf_name
--------------------------------------
1 0 root
2 1 US Blenders
3 2 US Blenders Chil11
4 1 Australian Blenderss
5 1 US Blenders Chil11 -2
40003 1 Child
40206 1 Child 111
40211 2 New
I want to display the folder hierarchy for 5 levels sepereated by '>' in drop drown so I wanted the result in following way
cf_id path
--------------------------------------
1 root
2 US Blenders
3 US Blenders > US Blenders Chil11
4 Australian Blenderss
5 US Blenders Chil11 -2
40003 Child
40206 Child 111
40211 US Blenders > New
I tried by writing following query but output result is not proper.
SELECT t.cf_id,
Group_concat(anc.cf_name ORDER BY anc.cf_name SEPARATOR ' > ') AS path
FROM content_folder AS t
JOIN content_folder AS anc
ON t.cf_name LIKE Concat(anc.cf_name, '%')
GROUP BY t.cf_id;
Can you please suggest me better solution. I am working on MySQL 5.7 so I think CTEs are not supported in this version.
I had done something like this with my database. Here is how i did it, i changed a few things and added the two update statements at the end to get your desired output, but i think you will get the gist of whats going on:
DECLARE #temp TABLE
(
id INT,
parent_id INT,
name NVARCHAR(100),
path NVARCHAR(MAX)
)
INSERT INTO #temp (id,parent_id,name) VALUES
(1,0,'root'),
(2,1,'US Blenders'),
(3,2,'US Blenders Chil11'),
(4,1,'Australian Blenderss'),
(5,1,'US Blenders Chil11 -2'),
(40003,1,'Child'),
(40206,1,'Child 111'),
(40211,2,'New')
DECLARE #counter TABLE ( id INT )
DECLARE #current_id INT;
DECLARE #loc_id INT;
INSERT INTO #counter SELECT id FROM #temp
SELECT * FROM #temp
WHILE (SELECT COUNT(*) FROM #counter) > 0
BEGIN
SET #current_id = (SELECT TOP 1 id FROM #counter)
SET #loc_id = (SELECT parent_id FROM #temp WHERE id = #current_id)
WHILE #loc_id IS NOT NULL AND #loc_id != 0
BEGIN
UPDATE #temp
SET path = ' > ' + (SELECT name FROM #temp WHERE id = #loc_id) + CASE WHEN path IS NULL THEN '' ELSE path END
WHERE id = #current_id
SET #loc_id = (SELECT parent_id FROM #temp WHERE id = #loc_id)
END
DELETE FROM #counter
WHERE id = #current_id
END
UPDATE #temp
SET path = REPLACE(REPLACE(path,' > root',''),' > ','')
UPDATE #temp
SET path = CASE WHEN LEN(path) > 0 THEN path + ' > ' + name ELSE name END
SELECT * FROM #temp
OUTPUT:
id parent_id name path
1 0 root root
2 1 US Blenders US Blenders
3 2 US Blenders Chil11 US Blenders > US Blenders Chil11
4 1 Australian Blenderss Australian Blenderss
5 1 US Blenders Chil11 -2 US Blenders Chil11 -2
40003 1 Child Child
40206 1 Child 111 Child 111
40211 2 New US Blenders > New

Select last complete block of rows

I have data similar to
ID Position
1 1
2 2
3 3
4 1
5 2
6 3
7 1
8 2
and I want to select the last set of rows that contain all positions, 1 through 3. By last I mean the set of rows with the highest value in the ID column. The desired result is
ID Position
4 1
5 2
6 3
How can i achieve this?
You can use this method, which find the last ID where the Position is 3, and gets the two rows preceding that. This assumes Position is sequential as it is in the sample data.
declare #table table (ID int identity(1,1), Position int)
insert into #table
values
(1),(2),(3),(1),(2),(3),(1),(2)
select top 3
*
from #table
where ID <=(
select max(ID)
from #table
where Position = 3)
order by ID desc
Or you could do this with an AND in your WHERE clause. I would store the ID as a variable so you only have to do the aggregation once though.
declare #id int = ( select max(ID)
from #table
where Position = 3)
select *
from #table
where ID <= #id
and ID >= #id - 2
order by ID

how to restart cursor from last end point

how can i restart this cursor after if condition and want row_number start from one again in answer.want to restart cursor from end point of if condition.
declare #SUM DECIMAL(20,2)
declare #EMPCODE varchar(10)
declare #NETPAY DECIMAL(10,2)
declare ABC scroll cursor for
select EMPCODE,NETPAY from TXXL where DATE='04-30-2015'
open ABC
declare #TEMP table(
EMPCODE varchar(10) primary key,
NETPAY DECIMAL(10,2),
SUM varchar(max)
)
set #SUM=0
fetch first from ABC into #EMPCODE,#NETPAY
while ##fetch_status=0
begin
fetch next from ABC into #EMPCODE,#NETPAY
SET #SUM=#SUM+#NETPAY
if(#SUM<=900000)
insert into #TEMP(EMPCODE,NETPAY,SUM) values(#EMPCODE,#NETPAY,#SUM)
end
select ROW_NUMBER() OVER(ORDER BY EMPCODE) ROWNUMBER,EMPCODE,NETPAY,SUM from #TEMP
close ABC
deallocate ABC
this cursor gives that answer,cursor stop working after 000179 EMPCODE because the sum of next empcode is greater than 900000 i want to start next empcode with ROWnumber 1 and sum like that the sum of this sample :-
ROWNUMBER EMPCODE NETPAY SUM
1 000066 29079.00 29079.00
2 000076 34515.00 63594.00
3 000084 15493.00 79087.00
4 000090 30399.00 109486.00
5 000109 25812.00 135298.00
6 000111 31453.00 166751.00
7 000113 30408.00 197159.00
8 000116 28040.00 225199.00
9 000124 27901.00 253100.00
10 000128 24445.00 277545.00
11 000131 35760.00 313305.00
12 000141 23345.00 336650.00
13 000150 48965.00 385615.00
14 000179 39350.00 424965.00
want answer like that:-
ROWNUMBER EMPCODE NETPAY SUM
1 000066 29079.00 29079.00
2 000076 34515.00 63594.00
3 000084 15493.00 79087.00
4 000090 30399.00 109486.00
5 000109 25812.00 135298.00
6 000111 31453.00 166751.00
7 000113 30408.00 197159.00
8 000116 28040.00 225199.00
9 000124 27901.00 253100.00
10 000128 24445.00 277545.00
11 000131 35760.00 313305.00
12 000141 23345.00 336650.00
13 000150 48965.00 385615.00
14 000179 39350.00 424965.00
1 000181 40814.00 40814.00
2 000182 24335.00 65149.00
3 000196 29439.00 94588.00
4 ----- ------ -------
5 -------- ----- -------

Implementing this join in SQL Stored procedure

I have 4 tables Position, Employee, Training and Trmatrix.
Table Position
PositionId PosName TrainingId
1 developer 1,2,3
2 Designer 4,5
3 BDA 2,3,6
Table Employee
Employeeid Ename Posid Courseid
1 Alex 1 4
2 Shaun 2 1,2,3
3 Hales 3
Table Training
Trainingid Trainingname
1 JAVA
2 Dot Net
3 PHP
4 Photoshop
5 JQUERY
6 Client Handling
TrMatrix
TrmatId TrID empID
1 1 1
2 2 1
3 3 1
4 4 1
5 4 2
6 5 2
7 1 2
8 2 2
9 2 3
10 3 3
foreign Key relation
trmatrix trId corresponds to the trainingID of the trainingtable.
Employee posid corresponds to the PositionId of the Positiontable.
Employee courseId corresponds to the trainingId of the trianingtable.
BY basic Aim is to get that course/trainingname which is no present in the
EMployee.Courseid column in correspondance to the trmatrix table,
which defines that I have to get the all entries from the trmatrix table for which there is no entry in the employee table Courseid column.
Suppose in case of Alex I have to fetch all the data from the trmatrix table except for course 4 since it is present in the courseid column of the Employee table, so it would return course no 1,2,3 not the no 4.
I am Newbie to the SQL so please help me out with this problem.
Thanks in advance
To start with, you should make PositionTraining and EmployeeCourse tables as well:
PositionTraining
PositionId TrainingId
1 1
1 2
1 3
2 4
2 5
3 2
3 3
3 6
EmployeeCourse
Employeeid Courseid
1 4
2 1
2 2
3 3
and then remove Position.TrainingId and Employee.Courseid.
By doing this you make the data much easier to query.
To get things which are not present in one table from another you can use
WHERE NOT EXISTS (SELECT value FROM OtherTable)
or
WHERE NOT IN (SELECT value FROM OtherTable)
However there is a class of queries called subqueries and these are very useful in this circumstance and a very good article on them is here
http://allenbrowne.com/subquery-01.html
(its written for ms access but the synstax and MS SQL rules are exactly the same so dont be put off)
UDF for spliting out entries
Create function [dbo].[atf_BarListToTable]
(#list ntext)
RETURNS #tbl TABLE (ListPosn int IDENTITY(1, 1) NOT NULL,
SString VARCHAR(1028) NOT NULL) AS
BEGIN
DECLARE #pos int
DECLARE #textpos int
DECLARE #ChunkLength smallint
DECLARE #str nvarchar(4000)
DECLARE #tmpstr nvarchar(4000)
DECLARE #leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #ChunkLength = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #ChunkLength))
SET #textpos = #textpos + #ChunkLength
SET #pos = charindex('|', #tmpstr)
WHILE #pos > 0
BEGIN
SET #str = substring(#tmpstr, 1, #pos - 1)
INSERT #tbl (SString) VALUES( #str)
SET #tmpstr = ltrim(substring(#tmpstr, #pos + 1, len(#tmpstr)))
SET #pos = charindex('|', #tmpstr)
END
SET #leftover = #tmpstr
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (SString) VALUES(#leftover)
RETURN
END