Get name from variable using index in T-SQL - sql-server

Using the following two queries
Query 1:
DECLARE #ContentColumnNamesSRC NVARCHAR(4000) = NULL,
SELECT
#ContentColumnNamesSRC = COALESCE(#ContentColumnNamesSRC + ', ', '') + '[' + name + ']'
FROM
tempdb.sys.columns
WHERE
1 = 1
AND object_id = OBJECT_ID('tempdb..#tempTable')
AND column_id < 9 -- First 8 columns are ID data, which is what I am after
Query 2:
DECLARE #ContentColumnNamesDST NVARCHAR(4000) = NULL,
SELECT
#ContentColumnNamesDST = COALESCE(#ContentColumnNamesDST + ', ', '') + '[' + name + ']'
FROM
tempdb.sys.columns
WHERE
1 = 1
AND object_id = OBJECT_ID('Import.dbo.ContentTable')
AND column_id < 9 -- First 8 columns are ID data, which is what I am after
I can get the first 8 columns from each table into a variable.
What I would like to do is find a way to get the values out of the variable, such that I can match the column names.
They should be identical in each table, and I need it to be able to create a dynamic merge statement, such that the columnsnames from each variable
#ContentColumnNamesSRC
and
#ContentColumnNamesDST
line up, so I can use it in a merge statement.
The point of this is to be able to use it in a loop, and all i would have to do is change which tables it looks at and the merge statements would still work.
Ideally, id like to end up with something like the following:
SELECT #StageSQLCore = N'USE Staging;
BEGIN TRANSACTION
MERGE '+#StageTableCore+' AS DST
USING '+#ImportTableCore+' AS SRC
ON (SRC.[Key] = DST.[Key])
WHEN NOT MATCHED THEN
INSERT ('+#StageTableCoreColumns+')
VALUES (
'+#ImportTableCoreColumns+',GETDATE())
WHEN MATCHED
THEN UPDATE
SET
DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.'+#ContentColumnNamesDST[i]' = SRC.'+#ContentColumnNamesSRC[i] +'
,DST.[ETLDate] = GETDATE()
;
COMMIT'
EXEC (#StageSQLCore)

You can generate Merge SQL like this if both the ordinal are matching
DECLARE #MergeSQL NVARCHAR(4000) = NULL
SELECT --*--,
#MergeSQL = COALESCE(#MergeSQL + ', DST.=', '') + QUOTENAME(bc.column_name) + ' = SRC.' + QUOTENAME(bc.COLUMN_NAME) + char(13)
FROM
test.INFORMATION_SCHEMA.COLUMNS tc
inner join testb.INFORMATION_SCHEMA.COLUMNS bc
on tc.TABLE_NAME = bc.TABLE_NAME
and tc.ORDINAL_POSITION = bc.ORDINAL_POSITION
and tc.TABLE_NAME = 'History'
WHERE
tc.ORDINAL_POSITION < 5 -- First 8 columns are ID data, which is what I am after
and bc.ORDINAL_POSITION < 5
select #MergeSQL

Related

How can I use LIKE operator instead of IN operator for multiple commas contains string in stored procedure

I just made the stored procedure for search items before the procedure I was doing this all via LINQ in C# e.g
//orders
if (objParam.OrderNumber != null && objParam.OrderNumber.Count > 0)
{
foreach (var orderNumber in objParam.OrderNumber)
{
orderNumbersBuilder.Append(',' + orderNumber.ToString());
}
}
var orderNbrs = orderNumbersBuilder.ToString().Trim(',').Split(',');
//Searching
(objParam.OrderNumber.Count == 0 || orderNbrs.Any(a => i.tblOrderMaster.OrderNumber.Contains(a)))
Now I want to do with the stored procedure. I'm getting the result with IN operator but I want to use LIKE operator e.g
SELECT * FROM tblOrderMaster WHERE TrxNumber LIKE '%' + (SELECT * FROM STRING_SPLIT('1330,1329',',')) + '%'
I've multiple filters so I don't want to use function and subqueries e.g
--Params
#Account NVARCHAR(MAX) = NULL,
#OrderNumber NVARCHAR(MAX) = NULL,
#Carrier NVARCHAR(MAX) = NULL,
#ItemCode NVARCHAR(MAX) = NULL,
#OrderType NVARCHAR(MAX) = NULL,
#PONumber NVARCHAR(MAX) = NULL,
#SONumber NVARCHAR(MAX) = NULL
--columns start
--columns end
--Where condtions
(#ACCOUNT IS NULL OR #Account = '' OR partners.PartnerCode IN (select * from string_split(#ACCOUNT,','))) -- multi select filters started
AND
(#OrderNumber IS NULL OR #OrderNumber = '' OR orderMaster.OrderNumber IN (select * from string_split(#OrderNumber,',')))
AND
(#Carrier IS NULL OR #Carrier = '' OR carrier.Description IN (select * from string_split(#Carrier,',')))
AND
(#ItemCode IS NULL OR #ItemCode = '' OR itemMaster.ItemCode IN (select * from string_split(#ItemCode,',')))
AND
(#OrderType IS NULL OR #OrderType = '' OR orderMaster.OrderType IN (select * from string_split(#OrderType,',')))
AND
(#PONumber IS NULL OR #PONumber = '' OR orderMaster.PONumber IN (select * from string_split(#PONumber,',')))
AND
(#SONumber IS NULL OR #SONumber = '' OR orderMaster.SONumber IN (select * from string_split(#SONumber,',')))
You would need to use subqueries; the fact you don't want to doesn't change this with your current design. Using the query with the literal values you have, it would look like this:
SELECT *
FROM dbo.tblOrderMaster OM
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT('1330,1329', ',') SS
WHERE OM.TrxNumber LIKE '%' + SS.[Value] + '%')
If you really don't want to use subqueries, then use table type parameters and then you can perform a JOIN:
SELECT OM.*
FROM dbo.tblOrderMaster OM
JOIN #YourTableVariable YTV ON OM.TrxNumber LIKE '%' + YTV.SearchValue + '%'

Use variable in WHERE clause

My SQL statement in SQL Server looks like this:
DECLARE #forecastYear AS varchar(5)
SET #forecastYear = '2020'
DECLARE #versionName AS varchar(25)
SET #versionName = '20201113_wk'
DECLARE #currentMonth AS varchar(2)
SET #currentMonth = (SELECT current_fc_month FROM tbl_current_month)
SELECT f.record_id
, u.it_ops_j_level_abbr_nm
, f.owner_nm
, f.unit_cd
, f.tbm_cd
, f.tower_nm
, f.description_txt
, f.comment_txt
, f.cost_pool_nm
, f.glac_nr
, f.glac_nm
, f.initiative_nm
, f.priority_nm
, f.growth_nm
, f.it_vendor_nm
, f.jan_amt
, f.feb_amt
, f.mar_amt
, f.apr_amt
, f.may_amt
, f.jun_amt
, f.jul_amt
, f.aug_amt
, f.sep_amt
, f.oct_amt
, f.nov_amt
, f.dec_amt
FROM tbl_new_forecast f
INNER JOIN tbl_unit_tree u
ON f.unit_cd = u.dept_id
WHERE f.version_nm = #versionName
AND f.status_cd = 'Approved'
AND f.entry_type = 'Forecast'
AND f.forecast_yr_id = #forecastYear
AND ABS(f.nov_amt)+ABS(f.dec_amt) <> 0
What I want to do is change the last statement in the WHERE clause based on the value in #currentMonth.
Therefore, if #currentMonth = '3' then the last statement would read
AND ABS(f.mar_amt)+ABS(f.apr_amt)+ABS(f.may_amt) <> 0
If #currentMonth = '7' then it would read
AND ABS(f.jul_amt)+ABS(f.aug_amt)+ABS(f.sep_amt) <> 0
I'm having a hard time figuring out how to accomplish this, because I get a SQL error with this syntax:
AND CASE
WHEN #currentMonth = '10' THEN ABS(f.oct_amt)+ABS(f.nov_amt)+ABS(f.dec_amt) <> 0
END
Any help is appreciated!
If you need a solution, a complex WHERE clause is an option. Note, that in T-SQL CASE is an expression, not a statement:
AND (
((#currentMonth = 1) AND (ABS(f.jan_amt) + ABS(f.feb_amt) + ABS(f.mar_amt) <> 0)) OR
((#currentMonth = 2) AND (ABS(f.feb_amt) + ABS(f.mar_amt) + ABS(f.apr_amt) <> 0)) OR
...
((#currentMonth = 10) AND (ABS(f.oct_amt) + ABS(f.nov_amt) + ABS(f.dec_amt) <> 0)) OR
((#currentMonth = 11) AND (ABS(f.nov_amt) + ABS(f.dec_amt) <> 0)) OR
((#currentMonth = 12) AND (ABS(f.dec_amt) <> 0))
)
Use dynamic SQL
DECLARE #month_dependent varchar (500)=' ';
DECLARE #main_query varchar(1000)=' ';
DECLARE #forecastYear AS varchar(5)
SET #forecastYear = '2020'
DECLARE #versionName AS varchar(25)
SET #versionName = '20201113_wk'
DECLARE #currentMonth char(2)
SET #currentMonth = (SELECT current_fc_month FROM tbl_current_month)
If #currentMonth = '3'
BEGIN set #month_dependent=' AND ABS(f.mar_amt)+ABS(f.apr_amt)+ABS(f.may_amt) <> 0 '; END
If #currentMonth = '7'
BEGIN set #month_dependent=' AND ABS(f.jul_amt)+ABS(f.aug_amt)+ABS(f.sep_amt) <> 0 '; END
set #main_query varchar(1000)=' SELECT f.record_id' +
' , u.it_ops_j_level_abbr_nm ' +
-- ' and all the rest of it! ' +
' FROM tbl_new_forecast f '+
' INNER JOIN tbl_unit_tree u '+
' ON f.unit_cd = u.dept_id ' +
' WHERE f.version_nm = '''+ #versionName + ''' '+
' AND f.status_cd = ''Approved'' '+
' AND f.entry_type = ''Forecast'' '+
' AND f.forecast_yr_id = ''' + #forecastYear + ''' '+
#month_dependent
EXECUTE sp_executesql #main_query ;
You can make this really complex by using a WHERE clause with a lot of ors in it:
AND ({first month condition} OR {Second month condition} OR {third month condition})
Etc. Another option is to place this into a stored procedure and use the month as the trigger to determine which statement to run. Depending on how you are running this, it might be a preferred method, as it can abstract out the details from the application (something you will want if you ever decide to normalize this data).
As for trying to use CASE in a WHERE clause, you have it wrong. The CASE WHEN has to equal something. The correct syntax is like:
AND SomeValue = CASE WHEN ...
You cannot simply use case, as a where is looking for equality (=), inequality (<>), and fuzzy values (LIKE). Thus, this does not work.
AND CASE WHEN ...
As an example, this shows something that fires back 1 to get equivalent rows. But you would need all of your conditions in here, which means the WHEN on month and the ABS() would be the entire condition. You then return 1 to indicate "found it". But you are running this as a monthly query, so filtering by the month and then determining the CASE ... WHEN is where you go.

SQL Server build dynamic sql

I have a temp table called #temp, and I need to get all the CDate column from that table, to build a string.
The CDate list in that table is (20171209, 20171210....20171223)
I expected to see
'A.[20171209] as [20171209], A.[20171210] as [20171210],
A.[20171211] as [20171211], A.[20171212] as [20171212],
A.[20171213] as [20171213], A.[20171214] as [20171214],
A.[20171215] as [20171215], A.[20171216] as [20171216],
A.[20171217] as [20171217], A.[20171218] as [20171218],
A.[20171219] as [20171219], A.[20171220] as [20171220],
A.[20171221] as [20171221], A.[20171222] as [20171222],
A.[20171223] as [20171223], '
however the result I got is missing the first date , ie 'A.[20171209] as [20171209]'
Here is my code:
SELECT
#col2 = ISNULL(#col2 + 'A.' + QUOTENAME(CDate) + ' as ' + QUOTENAME(CDate) + ', ' , '')
FROM
(SELECT DISTINCT CDate FROM #temp) AS tmp;
Your current approach will not work in some cases, it is an undocumented feature, always use For Xml path to concatenating the rows into csv.
SET #col2 = stuff((SELECT ', A.' + Quotename(CDate) + ' as '
+ Quotename(CDate)
FROM (SELECT DISTINCT CDate
FROM #temp) a
FOR xml path('')),1,1,'')

How to pivot dynamic number of columns row result into a verticle result list

There is a single row of data in a table that will have a varying number of columns named C1, C2, C3.. etc. I can locate that row but i want to un-pivot those dynamic number of columns values into a single column result... I have researched a ton on pivot/unpivot stuff but all examples i've found don't seem to handle the dynamic number of columns in the results.
Native Results:
Col 1 Col 2 Col 3 Col 4 Col 5.... Col X
Id Name DOB City State
Desired Results:
ColumnNameTBD:
Id
Name
DOB
City
State
Thank You!
Tim
After feedback from to the question i was able to piece together the solution. I now have a way to physically instanciate structure from dynamic/inconsistent structure automatically.
DECLARE #sColumnNames As Varchar(5000)
DECLARE #sSQL AS Varchar(5000);
--Flatten List of Column Names
Select #sColumnNames = ' ' + (
Select '(' + COLUMN_NAME + '),' As 'data()'
From SMAR_STG.INFORMATION_SCHEMA.COLUMNS With (NoLock)
where TABLE_NAME = 'V_ETL_CTX_COREM_EXCEL_DTL_COLUMNS'
For XML PATH('')
) + ' '
--Get Rid or trailing Comma
Set #sColumnNames = substring(#Output,1,len(#Output) -1)
-- Assemble SQL Statement
Set #sSQL = 'SELECT Upivot AS X
FROM SMAR_STG.DBO.V_ETL_CTX_COREM_EXCEL_DTL_COLUMNS
CROSS apply (VALUES '
Set #sSql = #sSQL + #sColumnNames
Set #sSQL = #sSQL + ') cs (upivot) '
-- Execute
EXEC (#sSql)
This item was already resolved here
As you will see from the article from the link, you will need to use COALESCE function to achive this

Update 13 million rows - SQL Server 2008

How can I update 13 million rows in stages by using a cursor or something?
Updating with the current script runs for days and still haven't finished.
There is a row_id field. 1 - 13m
Only one field needs to be updated.
UPDATE
[CIPC].[dbo].[tbldirector]
SET
[CIPC].[dbo].[tbldirector].ENT_NUM = REG.Ent_Number
FROM
[CIPC].[dbo].[tbldirector] DIR
INNER JOIN
[Cipc].[dbo].[tblregister] REG
ON
DIR.ENT_LONGNAME = REG.ENT_NAME
in this case you don't need cursor. You can do it with a loop like this.
DECLARE #indx int, #StepSize INT
SET #indx = 1
SET #StepSize = 100000
BEGIN TRAN
WHILE (EXISTS(SELECT 0 FROM [CIPC].[dbo].[tbldirector] WHERE row_id >= #indx))
BEGIN
PRINT 'Going to update indx ' + REPLICATE(CONVERT(VARCHAR, #indx) + ' -- ' + CONVERT(VARCHAR, #indx + #StepSize) + ' | ', 200)
UPDATE [CIPC].[dbo].[tbldirector]
SET [CIPC].[dbo].[tbldirector].ENT_NUM = REG.Ent_Number
FROM [CIPC].[dbo].[tbldirector] DIR
INNER JOIN [Cipc].[dbo].[tblregister] REG
ON DIR.ENT_LONGNAME = REG.ENT_NAME
WHERE row_id BETWEEN #indx AND #indx + #StepSize
SELECT #indx = #indx + #StepSize
SELECT REPLICATE(LEFT(CONVERT(VARCHAR, #indx) + ' | ', 10), 200)
END
COMMIT
This below link have three methods to do the needed,
http://ksadba.wordpress.com/2008/06/16/updating-millions-of-rows-merge-vs-bulk-collect/

Resources