cursor to get the desired result - sql-server

I have table like this:
x------x---------x----------------------x-------x
| Id | ACCTNO | name2 | QTY |
x------x---------x----------------------x-------x
| 1 | 1 | here is 2014-01-13 | 10 |
| 1 | 2 | there are 2014-01-12 | 20 |
| 2 | 3 | 2014-01-08 | 40 |
| 2 | 4 | 2014-01-06 | 30 |
x------x---------x----------------------x-------x
I'm trying to get the records where English alphabets are there along with date.
Im trying to do this like:
DECLARE #ACCTNO as INT;
DECLARE #name2 as varchar(max);
declare #st varchar(max)
DECLARE #BusinessCursor as CURSOR;
SET #BusinessCursor = CURSOR FOR
SELECT ACCTNO, Name2
FROM DPBENCHA2;
OPEN #BusinessCursor;
FETCH NEXT FROM #BusinessCursor INTO #ACCTNO, #name2;
--PRINT #Name2;
WHILE ##FETCH_STATUS = 0
BEGIN
set #st = #Name2;
select SUBSTRING(#st, patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9]%', #st), 50)
PRINT #st;
FETCH NEXT FROM #BusinessCursor INTO #ACCTNO,#st;
END
CLOSE #BusinessCursor;
DEALLOCATE #BusinessCursor;
Here instead of getting the records that contains English alphabet along with date. I'm getting all records. How to resolve this? How can I get the records only for account number 1 and 2? Is there any other ways to do it.

I'm trying to get the records where English alphabets are there along with date.
You seem to want a simple query:
SELECT ACCTNO, Name2
FROM DPBENCHA2
WHERE name2 LIKE '%[a-zA-Z]%';
This is much simpler than a cursor. In general, you want to avoid cursors in SQL.
EDIT:
If you want to extract the date from Name2:
SELECT ACCTNO, Name2,
SUBSTRING(name2,
PATINDEX('%[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'),
10) as thedate
FROM DPBENCHA2
WHERE name2 LIKE '%[a-zA-Z]%';

Use as following
SUBSTRING(#st, patindex('%[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%', #st), 50)

you can use a simple select, assuming your date format is yyyy-dd-mm or yyyy-mm-dd
select * from DPBENCHA2 where (name2 like '%[a-z]%')
and name2 like '% [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'
note the space in '%<space>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'
this will ensure that 'some alphabet 22323-12-34' will not be included in your select

Related

Delete a row from cursor while using the cursor

I have a cursor that iterates through my temporary table. While it's iterating, I want to check a condition and delete some rows depending on the condition (I will be deleting rows that the iterator has not reached yet).
I tried deleting rows from the table the cursor is iterating (so the temp table), but no success, I can see them in the Messages panel (I print its name).
Is it possible to delete rows from the table a cursor is iterating in SQL-Server ? If it's not, what are my alternatives ?
Basically, the temp table contains tree-like data and depending on the value of a column, I need to delete its children (and grand-children and so on) if it does not fit a criteria.
DECLARE cursor_name CURSOR
FOR (SELECT * FROM #test) ORDER BY Path
DECLARE
#Id AS INTEGER,
#Name AS VARCHAR(MAX),
#Path AS VARCHAR(MAX)
OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO #Id, #Name, #Path;
PRINT #Name
DELETE FROM #test
WHERE
Path LIKE '%76939%'
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM cursor_name INTO #Id, #Name, #Path;
PRINT #Name
END;
CLOSE cursor_name;
DEALLOCATE cursor_name;
#EDIT
Here is more detail on the problem. We have data structured like a tree list. Every item has multiple columns that specify some characteristics the row. Those characteristics can be inherited or not (if InheritanceFlag is 1, then it's inherited, if it's 0, then it is not).
So, when a user makes a change, we need to propagate the change to its children, depending on the said flag. If one of its child has the InheritanceFlag set to 0, then it won't change its value and neither will its children. I wanted to remove those rows with the cursor using the path.
Here is the data that I have. ParentID is the ID of its parent. In this case, suppose we are editing the item 76938, thus we are looking at its children. The ToEdit column is what I'm looking to create; with it, I can filter the rows and directly change the characteristic column to the new value.
+-------+----------+-------+-------------------------+-----------------+--------+
| ID | ParentID | Name | Path | InheritanceFlag | ToEdit |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76938 | NULL | 1 | (76938) | 1 | X |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76942 | 76938 | 1.1 | (76938)\(76942) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76952 | 76942 | 1.1.1 | (76938)\(76942)\(76952) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76961 | 76942 | 1.1.2 | (76938)\(76942)\(76961) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76943 | 76938 | 1.2 | (76938)\(76943) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76944 | 76938 | 1.3 | (76938)\(76944) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76946 | 76944 | 1.3.1 | (76938)\(76944)\(76946) | 1 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76947 | 76944 | 1.3.2 | (76938)\(76944)\(76947) | 0 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76948 | 76944 | 1.3.3 | (76938)\(76944)\(76948) | 1 | 0 |
+-------+----------+-------+-------------------------+-----------------+--------+
| 76945 | 76938 | 1.4 | (76938)\(76945) | 1 | 1 |
+-------+----------+-------+-------------------------+-----------------+--------+
You can delete from the underlying table and have the rows removed from future FETCHes if the cursor is DYNAMIC, and the query that defines the cursor doesn't require a spool, effectively turning it into a STATIC cursor.
In your code sorting by the unindexed VARCHAR(MAX) prevents the cursor from seeing any changes in the underlying table.
EG this
drop table if exists #test
go
create table #test(id integer, name varchar(max), path varchar(1000), index ix_path (path))
insert into #test(id,name,path) values (1,'a','0000000'),(2,'b', '0769391'),(3,'c', '1768391')
DECLARE cursor_name CURSOR DYNAMIC
FOR SELECT * FROM #test ORDER BY path
DECLARE
#Id AS INTEGER,
#Name AS VARCHAR(MAX),
#Path AS VARCHAR(MAX)
OPEN cursor_name;
FETCH NEXT FROM cursor_name INTO #Id, #Name, #Path;
PRINT #Name
print 'deleting'
DELETE FROM #test
WHERE
Path LIKE '%76939%'
WHILE 1=1
BEGIN
FETCH NEXT FROM cursor_name INTO #Id, #Name, #Path;
if ##FETCH_STATUS <> 0 break
PRINT #Name
END;
CLOSE cursor_name;
DEALLOCATE cursor_name;
outputs
(3 rows affected)
a
deleting
(1 row affected)
c

How do i can show forecast years data from row into column?

Suppose if Item-A's Sale in 2013 is 100 Quantity and I'm expecting 10% of sales growth in next year i.e. in 2014
--------------------
ITEM | YEAR | QTY |
--------------------
ITM-A| 2013 | 100 |
ITM-B| 2013 | 200 |
--------------------
if I want to forecast sale data for up to year 2015
------------------------------
Item | 2013 | 2014 | 2015 |
------------------------------
Item-A | 100 | 110 | 121 |--each year qty incremented by 10% of its
Item-B | 200 | 220 | 242 |--previous year qty
------------------------------
try this,you have to use dynamic sql
Declare #toyear int=2016
Declare #forcast int=10
Declare #t table (ITEM varchar(50), years int, qty int)
insert into #t
select 'TM-A' ITEM , 2013 years, 100 qty
union all
select 'TM-B' ITEM , 2013 years, 200 qty
;with CTE1 as
(
select * from #t
union all
select b.ITEM,b.years+1,b.qty+((#forcast*b.qty)/100) from #t a
inner join cte1 b on a.ITEM=b.ITEM
and b.years<#toyear
)
select * from
(select * from cte1 )t4
pivot(min(qty) for years in([2013],[2014],[2015],[2016]))pvt
Try this:
select item,
qty as '2013',
round(qty*1.1) as '2014',
round(qty*1.21) as '2015'
from sale;
A dynamic query using stored procedure
DELIMITER $$
create procedure p (IN end INT(10))
BEGIN
declare start int;
declare fact FLOAT;
SET fact = 1.1;
SELECT year into start FROM sale limit 1;
SET #QUERY1 = CONCAT("SELECT ITEM, QTY AS '",start,"'");
WHILE start < end DO
SET start = start + 1;
SET #QUERY1 = CONCAT(#QUERY1," ,qty*",fact," as '", start,"'");
SET fact = fact *1.1;
END WHILE;
SET #QUERY1 = CONCAT(#QUERY1," from sale");
PREPARE stmt FROM #QUERY1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
Output:
mysql> call p(2016);
+-------+------+-------+--------+---------+
| ITEM | 2013 | 2014 | 2015 | 2016 |
+-------+------+-------+--------+---------+
| itemA | 100 | 110.0 | 121.00 | 133.100 |
| itemB | 200 | 220.0 | 242.00 | 266.200 |
+-------+------+-------+--------+---------+
2 rows in set (0.00 sec)
Check this question:
Pass a function return to another in the same row
Your function is simply the multiplication by 1.1

Split into 3 characters in Sql Server

I have a string value in db. I need to insert a , in between each 3 characters. Here is the example string.
Sample String
111100120125 // This sample is a dynamic string. So It can be less or more ..
Result
Val = 111,100,120,125
Please tell me about the builtin SqlServer function to get the , seperated string as a result.
Any help to this will be appreciated.
Thanks.
Please tell me about the builtin SqlServer function to get the ,
seperated string as a result.
You can use Stuff() function. Fiddle demo specific to your given string.
Declare #S varchar(50) = '111100120125'
SELECT STUFF(STUFF(STUFF(#S,4,0,','),8,0,','),12,0,',')
111,100,120,125
EDIT: More generic solution would be to create a function like below (Note that this function starts inserting separator value from end):
CREATE FUNCTION dbo.AddSeparator (#String VARCHAR(max), #Separator VARCHAR(1))
RETURNS VARCHAR(max)
AS
BEGIN
Declare #Length int = Len(#String)
WHILE (#Length > 3)
BEGIN
SELECT #String = STUFF(#String, #Length - 2, 0, #Separator),
#Length = #Length -3
END
RETURN #String
END;
Usage and fiddle demo :
SELECT String Original, dbo.AddSeparator(String, ',') Modified
FROM T
Results:
| ORIGINAL | MODIFIED |
|----------|-----------|
| | |
| 1 | 1 |
| 12 | 12 |
| 123 | 123 |
| 1234 | 1,234 |
| 12345 | 12,345 |
| 123456 | 123,456 |
| 1234567 | 1,234,567 |
You can use recursive CTE:
declare #f varchar(100)='111100120125'
;with pos as(
select 3 n, cast(SUBSTRING(#f,1,3) as varchar(max)) a
union all
select n+3 n, a+','+ SUBSTRING(#f,n+1,3) from pos
where n<LEN(#f)
)
select max(#f) ini, max(a) res from pos;

Find primary key from one table in comma separated list

I've been given the task at work of creating a report based on a very poorly designed table structure.
Consider the following two tables. They contain techniques that each person likes to perform at each gym. Keep in mind that a unique person may show up on multiple rows in the PERSONNEL table:
PERSONNEL
+-----+-----+-------+--------+-----------+
| ID | PID | Name | Gym | Technique |
+-----+-----+-------+--------+-----------+
| 1 | 122 | Bob | GymA | 2,3,4 |
+-----+-----+-------+--------+-----------+
| 2 | 131 | Mary | GymA | 1,2,4 |
+-----+-----+-------+--------+-----------+
| 3 | 122 | Bob | GymB | 1,2,3 |
+-----+-----+-------+--------+-----------+
TECHNIQUES
+-----+------------+
| ID | Technique |
+-----+------------+
| 1 | Running |
+-----+------------+
| 2 | Walking |
+-----+------------+
| 3 | Hopping |
+-----+------------+
| 4 | Skipping |
+-----+------------+
What I am having trouble coming up with is a MSSQL query that will reliably give me a listing of every person in the table that is performing a certain technique.
For instance, let's say that I want a listing of every person that likes skipping. The desired results would be:
PREFERS_SKIPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 131 | Mary | GymA |
+-----+-------+--------+
Likewise hopping:
PREFERS_HOPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 122 | Bob | GymB |
+-----+-------+--------+
I can break out the strings easily in ColdFusion, but that isn't an option due to the size of the PERSONNEL table. Can anyone help?
I think this query looks cleaner:
SELECT p.*,
t.Technique as ParsedTechnique
FROM Personnel p
JOIN Techniques t
ON CHARINDEX((','+CAST(t.id as varchar(10))+','), (','+p.technique+',')) > 0
WHERE t.id ='1';
You can just change the WHERE t.id = to whatever TechniqueId you need.
Fiddle Here
Using this function
Create FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
You can query like this
declare #a Table (id int,Name varchar(10),Kind Varchar(100))
insert into #a values (1,'test','1,2,3,4'),(2,'test2','1,2,3,5'),(3,'test3','3,5')
Select a.ID,Name
from #a a
cross apply F_SplitAsIntTable(a.Kind) b
where b.ID=2
One of the problems you have to prevent is prevent "1" from matching "10" and "11". For this, you want to be sure that all values are delimited by the separator (in this case a comma).
Here is a method using like that should work effectively (although performance will not be so great):
SELECT p.*, t.Technique as ParsedTechnique
FROM Personnel p join
Techniques t
on ','+p.technique+',' like '%,'+cast(t.id as varchar(255))+',%'
WHERE t.id = 1;
If performance is an issue, then fix your data structure an include a PersonTechniques table so you can do a proper join.
The first comment under the question provided the link to the answer. Here's what I ended up going with:
WHERE
p.Technique LIKE '%,29,%' --middle
OR
p.Technique LIKE '29,%' --start
OR
p.Technique LIKE '%,29' --end
OR
p.Technique = '29' --single (good point by Cheran S in comment)
At initial glance I thought it wouldn't work, but clever use of % made it not match ids like 129, etc.

SSIS data manipulation

I am currently using SSIS to read the data from a table, modify a column and inset it into a new table.
The modification I want to perform will occur if a previously read row has an identical value in a particular column.
My original idea was to use a c# script with a dictionary containing previously read values and a count of how many times it has been seen.
My problem is that I cannot save a dictionary as an SSIS variable. Is it possible to save a C# variable inside an SSIS script component? or is there another method I could use to accomplish this.
As an example, the data below
/--------------------------------\
| Unique Column | To be modified |
|--------------------------------|
| X5FG | 0 |
| QFJD | 0 |
| X5FG | 0 |
| X5FG | 0 |
| DFHG | 0 |
| DDFB | 0 |
| DDFB | 0 |
will be transformed into
/--------------------------------\
| Unique Column | To be modified |
|--------------------------------|
| X5FG | 0 |
| QFJD | 0 |
| X5FG | 1 |
| X5FG | 2 |
| DFHG | 0 |
| DDFB | 0 |
| DDFB | 1 |
Rather than use a cursor, just use a set based statment
Assuming SQL 2005+ or Oracle, use the ROW_NUMBER function in your source query like so. What's important to note is the PARTITION BY defines your group/when the numbers restart. The ORDER BY clause directs the order in which the numbers are applied (most recent mod date, oldest first, highest salary, etc)
SELECT
D.*
, ROW_NUMBER() OVER (PARTITION BY D.unique_column ORDER BY D.unique_column ) -1 AS keeper
FROM
(
SELECT 'X5FG'
UNION ALL SELECT 'QFJD'
UNION ALL SELECT 'X5FG'
UNION ALL SELECT 'X5FG'
UNION ALL SELECT 'DFHG'
UNION ALL SELECT 'DDFB'
UNION ALL SELECT 'DDFB'
) D (unique_column)
Results
unique_column keeper
DDFB 0
DDFB 1
DFHG 0
QFJD 0
X5FG 0
X5FG 1
X5FG 2
You can create a script component. When given the choice, select the row transformation (instead of source or destination).
In the script, you can create a global variable that you will update in the process row method.
Perhaps SSIS isn't the solution for this one task. Using a cursor with a table-valued variable you would be able to accomplish the same result. I'm not a fan of cursors in most situation, but when you need to iterate through data that depends on previous iterations or is self-reliant then it can be useful. Here's an example:
DECLARE
#value varchar(4)
,#count int
DECLARE #dictionary TABLE ( value varchar(4), count int )
DECLARE cur CURSOR FOR
(SELECT UniqueColumn FROM SourceTable s)
OPEN cur;
FETCH NEXT FROM cur INTO #value;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #innerCount int = 0
IF NOT EXISTS (SELECT 1 FROM #dictionary WHERE value = #value)
BEGIN
INSERT INTO #dictionary ( value, count )
VALUES( #value, 0 )
END
ELSE
BEGIN
SET #innerCount = (SELECT count + 1 FROM #dictionary WHERE value = #value)
UPDATE #dictionary
SET count = #innerCount
WHERE value = #value
END
INSERT INTO TargetTable ( value, count )
VALUES (#value, #innerCount)
FETCH NEXT FROM cur INTO #value;
END

Resources