Need to repeat the result rows based on a column value - sql-server

I need a help in generating SQL result based on a column value to calculate per day amount.
I have a SQL like below.
select guid, reccharge, dayCareDaysCharged, dayCareHoursPerDay, RATE from tableA.
Here if reccharge = 0 (weekly) then the result should have 7 rows with RATE as RATE/7 for each row (per day amount);
if reccharge = 1 (monthly) then the result should have 30 rows with RATE as RATE/30 for each row;
if reccharge = 2 (yearly) then the result should have 365 rows with RATE as RATE/365 for each row;
if reccharge = 3 (hourly) then there should be a row as calculate it for a day;
How can I achieve this. Please help.

The question seems a little strange to me, but if I've followed it correctly then maybe this;
CREATE TABLE [#temp1] (reccharge tinyint,rdays smallint)
GO
declare #rcount smallint
set #rcount=0
while #rcount<7
begin
set #rcount=#rcount+1
insert #temp1 values (0,7)
end
set #rcount=0
while #rcount<30
begin
set #rcount=#rcount+1
insert #temp1 values (1,30)
end
set #rcount=0
while #rcount<365
begin
set #rcount=#rcount+1
insert #temp1 values (2,365)
end
insert #temp1 values (3,1)
GO
select t1.[guid], t1.reccharge, t1.dayCareDaysCharged, t1.dayCareHoursPerDay, t1.RATE/t2.rdays as RATE from tableA t1
inner join #temp1 t2 on t1.reccharge=t2.reccharge
order by t1.[guid]

I have not idea to do it in single query, but you can do it by using CURSOR as like below :
declare #reccharge int
declare #count int
declare #i int
declare #strSQL nvarchar(max) = 'select 1, 1 where 1 <> 1'
declare CurTest cursor for select reccharge from test
open CurTest
fetch next from CurTest into #reccharge
while ##fetch_status = 0
begin
set #i = 0;
select #count = case when #reccharge = 0 then 7 when #reccharge = 1 then 30 when #reccharge = 2 then 365 else 1 end
while #i < #count
begin
set #strSQL = #strSQL + ' union all select reccharge, Rate/' + cast(#count as varchar(10)) + ' from test where reccharge = ' + cast(#reccharge as varchar(10))
set #i = #i + 1;
end
fetch next from CurTest into #reccharge
end
close CurTest
deallocate CurTest
exec(#strSQL)

Related

Batch insert of data into a table

I have very big dataset about 40000000 items of data. I am trying to load this to a new table. Since the dataset is really big, I am trying to load them in batch so the log transaction would not be full. I am using the query below but still have issue with log transaction being full.
Is there a way to commit the transition after each batch so that it wont be saved in log and will go for next batch?
Please let me know if there is a way for me to solve this issue.
And SSIS is not an option for me currently.
DECLARE #I BIGINT;
DECLARE #icnt int;
DECLARE #n INT, #flag INT;
SELECT #icnt = (SELECT COUNT(*) FROM table1) A
PRINT #ICNT
BEGIN
SELECT #I = 0
WHILE #I <=#icnt
BEGIN
SELECT #n = 0
SELECT #flag = 1
BEGIN TRANSACTION
WHILE #flag != 0
BEGIN
;WITH A AS
(
SELECT
a.*,
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS batchnum
FROM
table1
)
INSERT INTO table2 (*)
SELECT *
FROM A
WHERE batchnum >= #I AND batchnum <#I + 10000000
SELECT #I = #I + 10000000
SELECT #n = #n + 10000000
SELECT #flag = CASE WHEN #n >= 50000000 THEN 0
WHEN #I >= #icnt THEN 0 ELSE 1 END
PRINT #I
END
COMMIT TRANSACTION
END
END

Adding a duplicate number

I'm currently experiencing the following problem.
If you have a look at the below test case:
declare #Count int
, #id int = 777
declare #table table
( id int identity primary key
, JCid int
, line int
, udf float )
insert into #table (jcid,line,udf)
values
(777,1,1),
(777,2,2.1),
(777,3,2.2),
(777,4,2),
(777,5,3)
select
#Count = count(left(L.udf,1))
from #table L
where L.jcid = #id
group by left(L.udf,1)
having count(left(L.udf,1))>1
select #Count
When I run this, I get the desired results of 3, however upon developing the below trigger, I can't get the count to work out correctly:
create trigger kv_trg_JobLineNumberUpdate_AW on _btblJCTxLines
after insert, update
as
declare #LineNum float
, #OldLine float
, #id int
, #Count int
, #Err nvarchar(500)
set #Err = '--------------------------';
set #Err = #Err + #Err + CHAR(10);
set #Err = #Err + CHAR(10);
set #Err = #Err + 'You are not allowed to change this Line Number!';
select
#LineNum = iLineID
, #OldLine = isnull(ufJCTxCMLineNumber,0)
, #id = iJCMasterID
from inserted
select
#Count = count(left(L.ufJCTxCMLineNumber,1))
from _btblJCTxLines L
join inserted i on left(L.ufJCTxCMLineNumber,1) = left(i.ufJCTxCMLineNumber,1)
where L.iJCMasterID = #id
group by left(L.ufJCTxCMLineNumber,1)
having count(left(L.ufJCTxCMLineNumber,1))>1
begin
if #OldLine = 0
begin
if #Count >= 2
begin
update _btblJCTxLines
set ufJCTxCMLineNumber = cast(#LineNum as varchar)+'.'+cast(#Count as varchar)
from _btblJCTxLines L
join inserted on L.idJCTxLines = inserted.idJCTxLines
end
else
begin
update _btblJCTxLines
set ufJCTxCMLineNumber = #LineNum
from _btblJCTxLines L
join inserted on L.idJCTxLines = inserted.idJCTxLines
end
end
else
begin
select
#OldLine = deleted.ufJCTxCMLineNumber
, #LineNum = inserted.ufJCTxCMLineNumber
from inserted, deleted
if (#OldLine <> #LineNum)
begin
raiserror(#Err, 16, 1)
rollback tran
return;
end
end
end
go
The ufJCTxCMLineNumber field is the duplicate number I'm looking for.
This triggers main purpose is to ensure that the ufJCTxCMLineNumber field never has duplicates.
What happens is, a user inserts a new line, when they add a new line above any current line, I want the ufJCTxCMLineNumber field to be updated to 3.1 depending on how many duplicates there is.
How would I go about getting the correct count?
Follow this link for Sample Data.

Convert a SQL function into a stored procedure

I am having trouble converting an UDF into a stored procedure.
Here is what I've got: this is the stored procedure that calls the function (I am using it to search for and remove all UNICODE characters that are not between 32 and 126):
ALTER PROCEDURE [dbo].[spRemoveUNICODE]
#FieldList varchar(250) = '',
#Multiple int = 0,
#TableName varchar(100) = ''
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL VARCHAR(MAX), #counter INT = 0
IF #Multiple > 0
BEGIN
DECLARE #Field VARCHAR(100)
SELECT splitdata
INTO #TempValue
FROM dbo.fnSplitString(#FieldList,',')
WHILE (SELECT COUNT(*) FROM #TempValue) >= 1
BEGIN
DECLARE #Column VARCHAR(100) = (SELECT TOP 1 splitdata FROM #TempValue)
SET #SQL = 'UPDATE ' + #TableName + ' SET ' + #Column + ' = dbo.RemoveNonASCII(' + #Column + ')'
EXEC (#SQL)
--print #SQL
SET #counter = #counter + 1
PRINT #column + ' was checked for ' + #counter + ' rows.'
DELETE FROM #TempValue
WHERE splitdata = #Column
END
END
ELSE IF #Multiple = 0
BEGIN
SET #SQL = 'UPDATE ' + #TableName + ' SET ' + #FieldList + ' = dbo.RemoveNonASCII(' + #FieldList + ')'
EXEC (#SQL)
--print #SQL
SET #counter = #counter + 1
PRINT #column + ' was checked for ' + #counter + ' rows.'
END
END
And here is the UDF that I created to help with the update (RemoveNonASCII):
ALTER FUNCTION [dbo].[RemoveNonASCII]
(#nstring nvarchar(max))
RETURNS varchar(max)
AS
BEGIN
-- Variables
DECLARE #Result varchar(max) = '',#nchar nvarchar(1), #position int
-- T-SQL statements to compute the return value
set #position = 1
while #position <= LEN(#nstring)
BEGIN
set #nchar = SUBSTRING(#nstring, #position, 1)
if UNICODE(#nchar) between 32 and 127
set #Result = #Result + #nchar
set #position = #position + 1
set #Result = REPLACE(#Result,'))','')
set #Result = REPLACE(#Result,'?','')
END
if (#Result = '')
set #Result = null
-- Return the result
RETURN #Result
END
I've been trying to convert it into a stored procedure. I want to track how many rows actually get updated when this is run. Right now it just says that all rows, however many I run this on, are updated. I want to know if say only half of them had bad characters. The stored procedure is already set up so that it tells me which column it is looking at, I want to include how many rows were updated. Here is what I've tried so far:
DECLARE #Result varchar(max) = '',#nchar nvarchar(1), #position int, #nstring nvarchar(max), #counter int = 0, #CountRows int = 0, #Length int
--select Notes from #Temp where Notes is not null order by Notes OFFSET #counter ROWS FETCH NEXT 1 ROWS ONLY
set #nstring = (select Notes from #Temp where Notes is not null order by Notes OFFSET #counter ROWS FETCH NEXT 1 ROWS ONLY)
set #Length = LEN(#nstring)
if #Length = 0 set #Length = 1
-- Add the T-SQL statements to compute the return value here
set #position = 1
while #position <= #Length
BEGIN
print #counter
print #CountRows
select #nstring
set #nchar = SUBSTRING(#nstring, #position, 1)
if UNICODE(#nchar) between 32 and 127
begin
print unicode(#nchar)
set #Result = #Result + #nchar
set #counter = #counter + 1
end
if UNICODE(#nchar) not between 32 and 127
begin
set #CountRows = #CountRows + 1
end
set #position = #position + 1
END
print 'Rows found with invalid UNICODE: ' + convert(varchar,#CountRows)
Right now I'm purposely creating a temp table and adding a bunch of notes and then adding in a bunch of invalid characters.
I created a list of 700+ Notes and then updated 2 of them with some invalid characters (outside the 32 - 127). There are a few that are null and a few that are not null, but that doesn't have anything in them. What happens is that I get 0 updates.
Rows found with invalid UNICODE: 0
Though it does see that the UNICODE for the one that it pulls is 32.
Obviously I'm missing something I just don't see what it is.
Here is a set based solution to handle your bulk replacements. Instead of a slow scalar function this is utilizing an inline table valued function. These are far faster than their scalar ancestors. I am using a tally table here. I keep this as a view on my system like this.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
If you are interested about tally tables here is an excellent article on the topic. http://www.sqlservercentral.com/articles/T-SQL/62867/
create function RemoveNonASCII
(
#SearchVal nvarchar(max)
) returns table as
RETURN
with MyValues as
(
select substring(#SearchVal, N, 1) as MyChar
, t.N
from cteTally t
where N <= len(#SearchVal)
and UNICODE(substring(#SearchVal, N, 1)) between 32 and 127
)
select distinct MyResult = STUFF((select MyChar + ''
from MyValues mv2
order by mv2.N
--for xml path('')), 1, 0, '')
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)'), 1, 0, '')
from MyValues mv
;
Now instead of being forced to call this every single row you can utilize cross apply. The performance benefit of just this portion of your original question should be pretty huge.
I also eluded to your string splitter also being a potential performance issue. Here is an excellent article with a number of very fast set based string splitters. http://sqlperformance.com/2012/07/t-sql-queries/split-strings
The last step here would be eliminate the first loop in your procedure. This can be done also but I am not entirely certain what your code is doing there. I will look closer and see what I can find out. In the meantime parse through this and feel free to ask questions about any parts you don't understand.
Here is what I've got working based on the great help from Sean Lange:
How I call the Stored Procedure:
exec spRemoveUNICODE #FieldList='Notes,Notes2,Notes3,Notes4,Notes5',#Multiple=1,#TableName='#Temp'
The #Temp table is created:
create table #Temp (ID int,Notes nvarchar(Max),Notes2 nvarchar(max),Notes3 nvarchar(max),Notes4 nvarchar(max),Notes5 nvarchar(max))
Then I fill it with comments from 5 fields from a couple of different tables that range in length from NULL to blank (but not null) to 5000 characters.
I then insert some random characters like this:
update #Temp
set Notes2 = SUBSTRING(Notes2,1,LEN(Notes2)/2) + N'￿㹊潮Ņ᯸ࢹᖈư㹨ƶ槹鎤⻄ƺ綐ڌ⸀ƺ삸)䀤ƍ샄)Ņᛡ鎤ꗘᖃᒨ쬵Ğᘍ鎤ᐜᏰ>֔υ赸Ƹ쳰డ촜)鉀௿촜)쮜)Ἡ屰山舰霡ࣆ 耏Аం畠Ư놐ᓜતᏛ֔Ꮫ֨Ꮫ꯼ᓜƒ 邰఍厰ఆ邰఍드)抉鎤듄)繟Ĺ띨)᯸ࢹ䮸ࣉ᯸ࢹ䮸ࣉ샰)ԌƏ￿

Nested While loops in SQL?

I want to change my SQL code which generates n table using two nested While loops, like:
DECLARE #count1 INT
SET #count1 = 2012
DECLARE #count2 INT
SET #count2 = 1
WHILE #count1 <= 2016
BEGIN
WHILE #count2 <= 12
create table LGDfigRecov as
select ...
from ...
WHERE FD0.mo_id=count2
AND FD0.an_id= count1
...
SET #count2 += 1
END
SET #count1 += 1
END
How can I change every time the name of new table like "LGDfigRecov +count1+count2"? It means I want to create every time a new table with the name of year and month at the end.
You could use a query like this to create your statements.
WITH
MonthNumbers AS (SELECT * FROM(VALUES('01'),('02'),('03'),('04'),('05'),('06'),('07'),('08'),('09'),('10'),('11'),('12')) AS x(MonthNr))
,YearNumbers AS (SELECT * FROM(VALUES('2012'),('2013'),('2014'),('2015'),('2016')) AS x(YearNr))
SELECT ROW_NUMBER() OVER(ORDER BY YearNr,MonthNr) AS SortInx
,CONCAT('CREATE TABLE LGDfigRecov_',YearNr,'_',MonthNr,' AS ', CHAR(13) + CHAR(10)) +
CONCAT('SELECT ... FROM ... ', CHAR(13) + CHAR(10)) +
CONCAT('WHERE FD0.mo_id=',MonthNr,' AND FD0.an_id=',YearNr,';') AS Cmd
FROM MonthNumbers
CROSS JOIN YearNumbers
I always try to avoid unnecessary loops and procedural approaches...
Check them if they are valid syntax (just copy the output in a new query window)
Open a CURSOR from this SELECT
Use the CURSOR to fetch this row-wise into a variable #Cmd
Use EXEC (#Cmd)
Below code will help to achieve your goal.
DECLARE #count1 INT,
#w_SQL nvarchar(4000);
SET #count1 = 2012
DECLARE #count2 INT
SET #count2 = 1
WHILE #count1 <= 2016
BEGIN
WHILE #count2 <= 12
SET #w_SQL = 'create table LGDfigRecov' + CONVERT(nvarchar(10), #count1) + CONVERT(nvarchar(10), #count2) + 'as
select ...
from ...
WHERE FD0.mo_id=count2
AND FD0.an_id= count1
...'
EXEC sp_executesql #w_SQL
SET #count2 += 1
END
SET #count1 += 1

Need to insert breaks in strings of very column with ' ' or ','

I have a table that has one Column but over 100,000 rows
Col_Name
qwchijhuirhxnihdiuyfnx
dhjhfiurhncnmxmzjcoinrds
xnbxknsiiuncirnxknrxnxz
I need to insert a '.' or '$' or some marker after every 3rd character
Example of result needed:
Col_Name
qwc.hij.hui.rhx.nih.diu.yfn.x
dhj.hfi.urh.ncn.mxm.zjc.oin.rds.
xnb.xkn.sii.unc.irn.xkn.rxn.xz
I originally solved this with:
INSERT INTO New_Table
(
c1
,c2
,c3
)
SELECT
substring(CAST(Col_Name AS VARCHAR(MAX)),1,3) as C1
,substring(CAST(Col_Name AS VARCHAR(MAX)),4,3) as C2
,substring(CAST(Col_Name AS VARCHAR(MAX)),7,3) as C3
From Table_Name
This causes problems later in the script so the data must remain in one column but could be inserted into a new table as long as it was a new table with just one column
Here's a sqlfiddle starting point you can refactor http://sqlfiddle.com/#!6/ab6dd/1/0 using function and while loop.
You may be able to do something more efficient with regular expressions or SQLCLR if you need speed.
CREATE FUNCTION dotify (#input varchar(MAX))
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #output varchar(MAX) = ''
declare #index int = 0
declare #length int
set #length = len(#input)
while #index <= #length
begin
SET #output = #output + substring(#input, #index, 1)
if (#index % 3) = 0 AND #index > 0
BEGIN
SET #output = #output +'.'
END
set #index = #index + 1
end
return(#output)
END
GO
select TOP 10000 col_name, dbo.dotify(col_name) FROM old_table
You can use TOP to limit the processing time to a few seconds so you can easily profile efficiency changes you make.

Resources