How I can remove the cursor from below query? + - sql-server

DECLARE #U_CHK TABLE (
ACCOUNT_ID NUMERIC
,START_NO NUMERIC
,END_NO NUMERIC
,SEQ_NO NUMERIC
,USED INT
)
DECLARE #ST_NO NUMERIC
,#END_NO NUMERIC
,#ACID NUMERIC
,#SEQ_NO NUMERIC
DECLARE cCurChk Scroll CURSOR
FOR
SELECT DISTINCT AC_ID
,ST_DOC_NO
,ST_DOC_NO
,END_DOC_NO
FROM AC_LVL_INVEN
GROUP BY
ST_DOC_NO
,END_DOC_NO
,AC_ID
OPEN cCurChk
FETCH NEXT FROM cCurChk INTO #ACID,#ST_NO,#SEQ_NO,#END_NO
WHILE ##Fetch_Status=0
BEGIN
WHILE (#SEQ_NO<=#END_NO)
BEGIN
INSERT INTO #U_CHK
VALUES
(
#ACID
,#ST_NO
,#END_NO
,#SEQ_NO
,0
)
SET #SEQ_NO = #SEQ_NO+1
END FETCH NEXT FROM cCurChk
INTO #ACID,#ST_NO,#SEQ_NO,#END_NO
END CLOSE cCurChk DEALLOCATE cCurChk
UpDate #U_CHK
SET USED = 1 FROM #U_CHK Ch INNER JOIN FA_Trans FA(NOLOCK) ON Fa.
Account_ID = Ch.Account_ID AND CONVERT(VARCHAR(10) ,Ch.Seq_No) = Fa.Instrument_No
WHERE FA.Status>4 AND STATUS<>12 AND Ac_Head_Type<>1 AND Trans_type = 'Debit'

You can use a merge statement to do the insert / update in place of the cursor. Here is an example
MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
WHEN NOT MATCHED BY TARGET THEN
INSERT (CustomerID, ProductID, LastPurchaseDate)
VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
Take a look at Technet - Inserting, Updating, and Deleting Data by Using MERGE

Related

Searching for multiple patterns in a string in T-SQL

In t-sql my dilemma is that I have to parse a potentially long string (up to 500 characters) for any of over 230 possible values and remove them from the string for reporting purposes. These values are a column in another table and they're all upper case and 4 characters long with the exception of two that are 5 characters long.
Examples of these values are:
USFRI
PROME
AZCH
TXJS
NYDS
XVIV. . . . .
Example of string before:
"Offered to XVIV and USFRI as back ups. No response as of yet."
Example of string after:
"Offered to and as back ups. No response as of yet."
Pretty sure it will have to be a UDF but I'm unable to come up with anything other than stripping ALL the upper case characters out of the string with PATINDEX which is not the objective.
This is unavoidably cludgy but one way is to split your string into rows, once you have a set of words the rest is easy; Simply re-aggregate while ignoring the matching values*:
with t as (
select 'Offered to XVIV and USFRI as back ups. No response as of yet.' s
union select 'Another row AZCH and TXJS words.'
), v as (
select * from (values('USFRI'),('PROME'),('AZCH'),('TXJS'),('NYDS'),('XVIV'))v(v)
)
select t.s OriginalString, s.Removed
from t
cross apply (
select String_Agg(j.[value], ' ') within group(order by Convert(tinyint,j.[key])) Removed
from OpenJson(Concat('["',replace(s, ' ', '","'),'"]')) j
where not exists (select * from v where v.v = j.[value])
)s;
* Requires a fully-supported version of SQL Server.
build a function to do the cleaning of one sentence, then call that function from your query, something like this SELECT Col1, dbo.fn_ReplaceValue(Col1) AS cleanValue, * FROM MySentencesTable. Your fn_ReplaceValue will be something like the code below, you could also create the table variable outside the function and pass it as parameter to speed up the process, but this way is all self contained.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION fn_ReplaceValue(#sentence VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #ResultVar VARCHAR(500)
DECLARE #allValues TABLE (rowID int, sValues VARCHAR(15))
DECLARE #id INT = 0
DECLARE #ReplaceVal VARCHAR(10)
DECLARE #numberOfValues INT = (SELECT COUNT(*) FROM MyValuesTable)
--Populate table variable with all values
INSERT #allValues
SELECT ROW_NUMBER() OVER(ORDER BY MyValuesCol) AS rowID, MyValuesCol
FROM MyValuesTable
SET #ResultVar = #sentence
WHILE (#id <= #numberOfValues)
BEGIN
SET #id = #id + 1
SET #ReplaceVal = (SELECT sValue FROM #allValues WHERE rowID = #id)
SET #ResultVar = REPLACE(#ResultVar, #ReplaceVal, SPACE(0))
END
RETURN #ResultVar
END
GO
I suggest creating a table (either temporary or permanent), and loading these 230 string values into this table. Then use it in the following delete:
DELETE
FROM yourTable
WHERE col IN (SELECT col FROM tempTable);
If you just want to view your data sans these values, then use:
SELECT *
FROM yourTable
WHERE col NOT IN (SELECT col FROM tempTable);

TSQL Dynamic Update of TOP(n) Rows

i have a table with one line per travel
second table has values of origin and destination and quantity for each combination
i need to run an update that run on the second table that update top N rows on the first table (n comes from the second table)
im using this update script inside a cursor in a procedure :
ALTER PROCEDURE [dbo].[SP_Travels_History_FromMissing]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #C_Day_Type_Code INT = NULL
DECLARE #C_PartOfDay_Code INT = NULL
DECLARE #C_Station_From INT = NULL
DECLARE #C_DestinationStation INT = NULL
DECLARE #C_Deploy INT = NULL
TRUNCATE TABLE [TCK_TMP_Fact_ALL_Travels_History_FromMissing]
INSERT
INTO [TCK_TMP_Fact_ALL_Travels_History_FromMissing]
SELECT TRV.Day_Type_Code,
TRV.PartOfDay_Code,
HIS.Station_From,
TRV.DestinationStation,
-- TRV.TotalTravels,
-- HIS.Percentage,
-- (TRV.TotalTravels * HIS.Percentage) / 100,
CAST(CEILING((TRV.TotalTravels * HIS.Percentage) / 100) AS INT) Deploy
FROM
(
SELECT DAT.Day_Type_Code,
-- DATEPART(HOUR,[EntranceDate]) EntranceHour,
POD.PartOfDay_Code,
DestinationStation,
COUNT(*) TotalTravels
FROM [Tickets].[dbo].[TCK_STG_Fact_Contract_Magnetic_Travels1] TRV
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Date] DAT ON CAST(TRV.[ExitDate] AS DATE) = CAST(DAT.Date AS DATE)
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Manual_PartOfDay] POD ON DATEPART(HOUR,TRV.[ExitDate]) = POD.PartOfDay_Hour
WHERE [EventID_Entrance] = -1
GROUP BY DAT.Day_Type_Code,
POD.PartOfDay_Code,
DestinationStation
) TRV
LEFT OUTER JOIN
(
SELECT *
FROM [dbo].[TCK_DWH_Fact_ALL_Travels_History_FromMissing]
WHERE [Total_Travels] IS NOT NULL
) HIS ON HIS.Day_Type_Code = TRV.Day_Type_Code
AND HIS.PartOfDay_Code = TRV.PartOfDay_Code
AND HIS.Station_To = TRV.DestinationStation
WHERE CAST(CEILING((TRV.TotalTravels * HIS.Percentage) / 100) AS INT) > 0
ORDER BY 1,2,4,5 DESC
DECLARE Missing_Cursor CURSOR FOR
SELECT [Day_Type_Code],
[PartOfDay_Code],
DestinationStation,
Station_From,
[Deploy]
FROM [Tickets].[dbo].[TCK_TMP_Fact_ALL_Travels_History_FromMissing]
ORDER BY [Day_Type_Code],
[PartOfDay_Code],
DestinationStation,
[Deploy] DESC,
Station_From
OPEN Missing_Cursor
FETCH NEXT
FROM Missing_Cursor
INTO #C_Day_Type_Code,
#C_PartOfDay_Code,
#C_DestinationStation,
#C_Station_From,
#C_Deploy
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE TOP (#C_Deploy) TRV
SET TRV.SourceStationID = #C_Station_From,
TRV.MissingStationSourceCode = 888
FROM [Tickets].[dbo].TCK_STG_Fact_Contract_Magnetic_Travels1 TRV
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Date] DAT ON CAST(TRV.[ExitDate] AS DATE) = CAST(DAT.Date AS DATE)
LEFT OUTER JOIN [Tickets].[dbo].[TCK_DWH_Dim_Manual_PartOfDay] POD ON DATEPART(HOUR,TRV.[ExitDate]) = POD.PartOfDay_Hour
WHERE DAT.Day_Type_Code = #C_Day_Type_Code
AND POD.PartOfDay_Code = #C_PartOfDay_Code
AND TRV.DestinationStation = #C_DestinationStation
AND TRV.SourceStationID IS NULL
-- COMMIT;
-- ORDER BY TRV.ContractID
-- LIMIT #C_Deploy;
-------------------------
FETCH NEXT
FROM Missing_Cursor
INTO #C_Day_Type_Code,
#C_PartOfDay_Code,
#C_DestinationStation,
#C_Station_From,
#C_Deploy
END
CLOSE Missing_Cursor
DEALLOCATE Missing_Cursor
END
Problem is It Takes more then 30 minutes
how can i use a dynamic update without a cursor?
any other suggestions are welcome

Multiple rows to single row in Azure Data Warehouse

As Azure DW is not supporting FOR XML and SELECT variable assignment, is there any other way to convert multiple rows into single row except using CURSOR?
I didn't found any direct method however the below code is working for me.
DECLARE #intColumnCount INT,
#intProcessCount INT,
#varColList VARCHAR(max)
SET #varColList = ''
IF Object_id('tempdb.dbo.#tempColumnNames') IS NOT NULL
BEGIN
DROP TABLE #tempcolumnnames;
END
CREATE TABLE #tempcolumnnames
(
intid INT,
varcolumnnames VARCHAR(256)
)
INSERT INTO #tempcolumnnames
SELECT Row_number()
OVER (
ORDER BY NAME),
NAME
FROM sys.tables
SET #intProcessCount = 1
SET #intColumnCount = (SELECT Count(*)
FROM #tempcolumnnames)
WHILE ( #intProcessCount <= #intColumnCount )
BEGIN
SET #varColList = #varColList + ', '
+ (SELECT varcolumnnames
FROM #tempcolumnnames
WHERE intid = #intProcessCount)
SET #intProcessCount +=1
END
SELECT Stuff(#varColList, 1, 2, '')
Hope this helps someone.

SQL Merge returning all the rows from target on match

I have the following SQL Merge statement.
DECLARE #TmpTable TABLE (BusinessBaseID int, BatchID uniqueidentifier, SupplierID int, SupplierVenueID varchar(200), AddressID int,[Action] varchar(50))
DECLARE #noop int; -- needed for the NO-OP below
Declare #TestOp Varchar(max)
Set #TestOp = 't'
-- Insert into BusinessBase retrieving all inserted BusinessBaseIDs mappings via tmptable
-- Another SQL blck goes here to insert records into TEMP table
MERGE Business.Address AS t
USING (SELECT tmp.BusinessBaseID, tmp.BatchID, tmp.SupplierID, tmp.SupplierVenueID,
v.Name, v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode,
v.Latitude,
v.Longitude,
dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) as VenueId
FROM #TmpTable as tmp INNER JOIN Supplier.VenueImport as v
ON tmp.BatchID = v.BatchID AND tmp.SupplierID = v.SupplierID AND tmp.SupplierVenueID = v.SupplierVenueID
WHERE (tmp.BatchID = 'D7F369F1-A66A-4440-8D4B-2F521F672916') AND (tmp.SupplierID = 17)
) AS s
ON S.VenueId >0
WHEN MATCHED THEN
UPDATE SET #TestOp = #TestOp + ':' +convert(varchar, S.VenueId)+'|'+Convert(varchar,t.AddressId) -- the NO-OP instead of update
WHEN NOT MATCHED BY TARGET THEN
INSERT (AddressLine1, AddressLine2, City, StateProvince,PostalCode,CountryCode,Lat,Long)
VALUES (AddressLine1, AddressLine2, City, County, PostalCode, 'GB', Latitude, Longitude)
OUTPUT s.BusinessBaseID, s.BatchID, s.SupplierID, s.SupplierVenueID,ISNULL(INSERTED.AddressID,deleted.addressId),$action INTO #TmpTable;
Select #TestOp;
Select #Temp where [Action] = 'Update'
Above query returning all the rows (except newly inserted records). Where as it suppose to return only 1 record as S.VenueId is greater than 0 for only one record.
dbo.GetVenueId is a function which returns an integer. It will be > 0 for existing records and -1 for not existing records.
Could somebody point me where I am doing wrong.
Thanks,
Naresh
Change the call dbo.GetVenueId to the following
CASE dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) WHEN -1 THEN -1 ELSE 0 END as VenueId
So It will act well

How can i build array or table in a function and return it in SQL Server 2008

I am using SQL Server 2008 and making a function that selects some records
i need to filter these records and return some records
CREATE PROCEDURE getPlayerLeft #minDate datetime ,#maxDate datetime ,#minLevel integer, #MaxLevel integer
AS
declare #acc_id integer
declare #lv integer
DECLARE db_cursor CURSOR FOR
SELECT DISTINCT account_id, MAX(lv) AS MAXLV
FROM Character
WHERE (logout_time BETWEEN #minDate AND #maxDate) AND (lv BETWEEN #minLevel AND #MaxLevel)
GROUP BY account_id
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #acc_id,#lv
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT Character.account, Character.race, Character.job, Character.job_1, Account.email
FROM Character, Account
WHERE (Character.account_id = #acc_id) AND (Character.lv = #lv) AND (Account.account_id = #acc_id)
//here i need to filter these selected record in some condition
//and save them in some collection (if existed) and return it
FETCH NEXT FROM db_cursor
INTO #acc_id,#lv
END
CLOSE db_cursor
DEALLOCATE db_cursor
Thanks a lot!
Write an Inline Function that uses a Common Table Expression or subquery.
CREATE FUNCTION getPlayerLeft (#minDate datetime, #maxDate datetime,
#minLevel int, #MaxLevel int)
RETURNS TABLE AS RETURN
WITH maxlevel AS
(
SELECT account_id, MAX(lv) AS lv
FROM dbo.Character
WHERE (logout_time BETWEEN #minDate AND #maxDate)
AND (lv BETWEEN #minLevel AND #MaxLevel)
GROUP BY account_id
)
SELECT Character.account, Character.race, Character.job,
Character.job_1, Account.email
FROM dbo.Character
INNER JOIN dbo.Account ON Account.account_id = account_id
INNER JOIN maxlevel ON maxlevel.account_id = Character.account_id
AND maxlevel.lv = Character.lv
GO
SELECT account, race, job, job_1, email
FROM dbo.getPlayerLeft('2010-01-01', '2010-12-31', 1, 5)
Your stored proc could look like this. No need for a CURSOR
Note the proper use of JOIN too.
SELECT
Filtered.*
FROM
(
SELECT DISTINCT
account_id, MAX(lv) AS MAXLV
FROM
Character
WHERE
(logout_time BETWEEN #minDate AND #maxDate) AND
(lv BETWEEN #minLevel AND #MaxLevel)
GROUP BY account_id
) FilterOne
JOIN
(
SELECT
Character.account_id, Character.lv,
Character.account, Character.race, Character.job, Character.job_1,
Account.email
FROM
Character
JOIN
Account ON Character.account_id = Account.account_id
) Filtered
ON FilterOne.account_id = Filtered.account_id AND FilterOne.MAXLV = Filtered.lv
WHERE
--add condition here?
SELECT * INTO #TempTable from [YOUR Query]
This will insert your records in a temp table then the last statement of your Procedure should be
SELECT * FROM #TempTable
You can return table variables from udfs. That link also has an example of a udf which returns one, scroll down to "An Example: Split".
You could also use temporary tables, but make sure you create the temp table before you call your sproc as exiting a sproc causes temp tables created in it to be dropped.

Resources