Comparing VARCHARs of different length - sql-server

I'm writing a stored procedure and I need to compare a SQL Server variable to a column. The problem, is that if my SQL variable is a VARCHAR that's not the same length as the column type, it won't match.
Example:
Let's say, I have a table types with a column name that has a type VARCHAR(100). Running the code below won't give me any results:
DECLARE #type VARCHAR(20);
SET #type = 'My Type Name';
select * from types where name = #type
However, running this query will find my column:
DECLARE #type VARCHAR(100);
SET #type = 'My Type Name';
select * from types where name = #type
Now, I'd like to be able to do it the first way, especially because if I modify the column, I don't want this query to start failing. Can anyone tell me what I'm doing wrong?
Edit:
Here's my column schema:
Query:
DECLARE #event_type VARCHAR(20);
SET #event_type = 'Price Increase Notification';
select * from events.types where name = #event_type
Output:
Query:
DECLARE #event_type VARCHAR(100);
SET #event_type = 'Price Increase Notification';
select * from events.types where name = #event_type
Output:

The following will behave as you described:
CREATE TABLE #types
(
type VARCHAR(100)
)
INSERT INTO #types (type) VALUES ('My Type Name')
-- returns 1 row
select * from #types
-- returns 0 rows
DECLARE #type VARCHAR(10);
SET #type = 'My Type Name';
select * from #types where type = #type
-- returns 1 row
DECLARE #long_type VARCHAR(100);
SET #long_type= 'My Type Name';
select * from #types where type = #long_type
drop table #types
The point is that DECLARE #type VARCHAR(10); SET #type = 'My Type Name'; will actually set your variable to 'My Type Na' (length = 10).
So, if your value is longer than your variable, the variable is set to the truncated value. No warning, no error. If you then try to equate it to the original (longer) value, you'll find they aren't equal.

Your edited example:
DECLARE #event_type VARCHAR(20);
SET #event_type = 'Price Increase Notification';
select * from events.types where name = #event_type
The string you are setting #event_type to is 27 characters so it is getting truncated to the 20 characters and therefore there is no match.
What I would probably do to ensure if the column length changes you are ok is declare #event_type as varchar(max)

here is what I would do
select * from events.types where ltrim(rtrim(name)) = #event_type

Related

SQL Server: get column value from string column name, assign value to variable

In SQL Server in a stored procedure, I want to get the value in a column of a single-row table given the column name, and assign that value to a variable. The column name may be different every time (I use T-SQL to interrogate the schema at run time).
The example given below is as minimal as I can make it, the important thing is that you cannot assume that the column name will always be entity_client, it could be anything at all, though (due to interrogation of INFORMATION SCHEMA) we will have the value assigned to the variable #entity_column_name.
Example preparation SQL:
IF OBJECT_ID('tempdb..#foo') IS NOT NULL
BEGIN;
DROP TABLE #foo;
END;
CREATE TABLE #foo
(
id INT,
entity_client NVARCHAR(255)
);
INSERT INTO #foo VALUES (1, 'clientcode|client_number');
DECLARE #entity_column_name NVARCHAR(255) = 'entity_client';
DECLARE #entity_column_value NVARCHAR(255);
I have tried the following:
SELECT TOP 1 #entity_column_name = [#entity_column_value]
FROM #foo;
...which generates an error
Invalid column name '#entity_column_value'
I have also tried:
EXEC('SELECT TOP 1 #entity_column_value = [' + #entity_column_name + '] FROM #foo;');
which generates another error
Must declare the scalar variable "#entity_column_value"
The following works, but unfortunately the column name is hard-coded - I wanted to be able to vary the column name:
SELECT TOP 1 #entity_column_value = [entity_client]
FROM #foo;
Yes, I have looked on SO and found the following questions, but they do not provide an answer where the value is assigned to a variable, in both cases the SELECT output is simply dumped to screen:
Get column value from string column name sql
Get column value from dynamically achieved column name
This will actually work but you need to declare the output variable:
DECLARE #entity_column_name NVARCHAR(255) = 'entity_client';
DECLARE #entity_column_value NVARCHAR(255);
DECLARE #tsql NVARCHAR(1000) = 'SELECT TOP 1 #entity_column_value = [' + #entity_column_name + '] FROM #foo;'
EXEC sp_executesql #tsql, N'#entity_column_value NVARCHAR(255) OUTPUT',
#entity_column_value OUTPUT;

SQL Server 2014 using variables in CONTAINS error Msg 7645 Null or empty full-text predicate

I'm working with SQL Server 2014 Express with FileTable. I'm trying to select documents from the filetable (unstructured, pdf) based on whether they contain specific keywords based on the registration mark of an aircraft:
--SET VARIABLE FOR A REGISTRATION MARK
DECLARE #Reg nvarchar(10)
SET #Reg = 'PH-BGA'
--SET VARIABLE FOR MANUFACTURER OF REGISTRATION MARK
DECLARE #Manufacturer nvarchar(10) = NULL
SELECT FleetInfoKLM.Manufacturer FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg;
SET #Manufacturer = '""'
--SET VARIABLE FOR FAMILY OF REGISTRATION MARK
DECLARE #Family nvarchar(10) = NULL
SELECT FleetInfoKLM.Family FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg;
SET #Family = '""'
--SET VARIABLE FOR SERIES OF REGISTRATION MARK
DECLARE #Series nvarchar(10) = NULL
SELECT FleetInfoKLM.Series FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg;
SET #Series = '""'
--SELECT MAINTENANCE DOCUMENTS BASED ON VARIABLES FOR MANUFACTURER, FAMILY AND SERIES
SELECT MaintenanceDocumentation.name FROM MaintenanceDocumentation
WHERE ((#Manufacturer = '""') AND CONTAINS(file_stream, #Manufacturer))
AND ((#Family = '""') AND CONTAINS(file_stream, #Family))
AND ((#Series = '""') AND CONTAINS(file_stream, #Series))
Without SET #VARIABLE = '""' I get the error Msg 7645 Null or empty full-text predicate. Hence I added the SET #VARIABLE = '""' as it was found as a solution in this question 7645 Null or empty full-text predicate. However, now the result set is empty (no documents found), while I know that there should be results. The variables are properly set, in this example as Boeing, 737 and 800 respectively. If I replace the variables as a string (e.g. 'Boeing') as an argument in CONTAINS, I do get the expected results.
Anyone got a clue what the problem is? Have been Googling for hours without result other than avoiding the error with SET #VARIABLE = '""'.
Okay, I finally fixed it by altering the SET statement to SET #variable = (SELECT ..):
--SET VARIABLE FOR A REGISTRATION MARK
DECLARE #Reg nvarchar(10);
SET #Reg = 'PH-BGO';
--SET VARIABLE FOR MANUFACTURER OF REGISTRATION MARK
DECLARE #Manufacturer nvarchar(100);
SET #Manufacturer = (SELECT FleetInfoKLM.Manufacturer FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg);
--SET VARIABLE FOR FAMILY OF REGISTRATION MARK
DECLARE #Family nvarchar(100);
SET #Family = (SELECT FleetInfoKLM.Family FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg);
--SET VARIABLE FOR SERIES OF REGISTRATION MARK
DECLARE #Series nvarchar(100);
SET #Series = (SELECT FleetInfoKLM.Series FROM FleetInfoKLM WHERE FleetInfoKLM.Reg = #Reg);
--SHOW LOCAL VARIABLES VALUES
SELECT #Manufacturer;
SELECT #Family;
SELECT #Series;
--SELECT MAINTENANCE DOCUMENTS BASED ON VARIABLES FOR MANUFACTURER, FAMILY AND SERIES
SELECT MaintenanceDocumentation.name FROM MaintenanceDocumentation
WHERE MaintenanceDocumentation.path_locator IS NOT NULL
AND CONTAINS(file_stream, #Manufacturer)
AND CONTAINS(file_stream, #Family)
AND CONTAINS(file_stream, #Series);
No need for the SET #variable = '""' statements.

Must declare the scalar variable

I wrote this SQL in a stored procedure but not working,
declare #tableName varchar(max) = 'TblTest'
declare #col1Name varchar(max) = 'VALUE1'
declare #col2Name varchar(max) = 'VALUE2'
declare #value1 varchar(max)
declare #value2 varchar(200)
execute('Select TOP 1 #value1='+#col1Name+', #value2='+#col2Name+' From '+ #tableName +' Where ID = 61')
select #value1
execute('Select TOP 1 #value1=VALUE1, #value2=VALUE2 From TblTest Where ID = 61')
This SQL throws this error:
Must declare the scalar variable "#value1".
I am generating the SQL dynamically and I want to get value in a variable. What should I do?
The reason you are getting the DECLARE error from your dynamic statement is because dynamic statements are handled in separate batches, which boils down to a matter of scope. While there may be a more formal definition of the scopes available in SQL Server, I've found it sufficient to generally keep the following three in mind, ordered from highest availability to lowest availability:
Global:
Objects that are available server-wide, such as temporary tables created with a double hash/pound sign ( ##GLOBALTABLE, however you like to call # ). Be very wary of global objects, just as you would with any application, SQL Server or otherwise; these types of things are generally best avoided altogether. What I'm essentially saying is to keep this scope in mind specifically as a reminder to stay out of it.
IF ( OBJECT_ID( 'tempdb.dbo.##GlobalTable' ) IS NULL )
BEGIN
CREATE TABLE ##GlobalTable
(
Val BIT
);
INSERT INTO ##GlobalTable ( Val )
VALUES ( 1 );
END;
GO
-- This table may now be accessed by any connection in any database,
-- assuming the caller has sufficient privileges to do so, of course.
Session:
Objects which are reference locked to a specific spid. Off the top of my head, the only type of session object I can think of is a normal temporary table, defined like #Table. Being in session scope essentially means that after the batch ( terminated by GO ) completes, references to this object will continue to resolve successfully. These are technically accessible by other sessions, but it would be somewhat of a feat do to so programmatically as they get sort of randomized names in tempdb and accessing them is a bit of a pain in the ass anyway.
-- Start of session;
-- Start of batch;
IF ( OBJECT_ID( 'tempdb.dbo.#t_Test' ) IS NULL )
BEGIN
CREATE TABLE #t_Test
(
Val BIT
);
INSERT INTO #t_Test ( Val )
VALUES ( 1 );
END;
GO
-- End of batch;
-- Start of batch;
SELECT *
FROM #t_Test;
GO
-- End of batch;
Opening a new session ( a connection with a separate spid ), the second batch above would fail, as that session would be unable to resolve the #t_Test object name.
Batch:
Normal variables, such as your #value1 and #value2, are scoped only for the batch in which they are declared. Unlike #Temp tables, as soon as your query block hits a GO, those variables stop being available to the session. This is the scope level which is generating your error.
-- Start of session;
-- Start of batch;
DECLARE #test BIT = 1;
PRINT #test;
GO
-- End of batch;
-- Start of batch;
PRINT #Test; -- Msg 137, Level 15, State 2, Line 2
-- Must declare the scalar variable "#Test".
GO
-- End of batch;
Okay, so what?
What is happening here with your dynamic statement is that the EXECUTE() command effectively evaluates as a separate batch, without breaking the batch you executed it from. EXECUTE() is good and all, but since the introduction of sp_executesql(), I use the former only in the most simple of instances ( explicitly, when there is very little "dynamic" element of my statements at all, primarily to "trick" otherwise unaccommodating DDL CREATE statements to run in the middle of other batches ). #AaronBertrand's answer above is similar and will be similar in performance to the following, leveraging the function of the optimizer when evaluating dynamic statements, but I thought it might be worthwhile to expand on the #param, well, parameter.
IF NOT EXISTS ( SELECT 1
FROM sys.objects
WHERE name = 'TblTest'
AND type = 'U' )
BEGIN
--DROP TABLE dbo.TblTest;
CREATE TABLE dbo.TblTest
(
ID INTEGER,
VALUE1 VARCHAR( 1 ),
VALUE2 VARCHAR( 1 )
);
INSERT INTO dbo.TblTest ( ID, VALUE1, VALUE2 )
VALUES ( 61, 'A', 'B' );
END;
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR( MAX ),
#PRM NVARCHAR( MAX ),
#value1 VARCHAR( MAX ),
#value2 VARCHAR( 200 ),
#Table VARCHAR( 32 ),
#ID INTEGER;
SET #Table = 'TblTest';
SET #ID = 61;
SET #PRM = '
#_ID INTEGER,
#_value1 VARCHAR( MAX ) OUT,
#_value2 VARCHAR( 200 ) OUT';
SET #SQL = '
SELECT #_value1 = VALUE1,
#_value2 = VALUE2
FROM dbo.[' + REPLACE( #Table, '''', '' ) + ']
WHERE ID = #_ID;';
EXECUTE dbo.sp_executesql #statement = #SQL, #param = #PRM,
#_ID = #ID, #_value1 = #value1 OUT, #_value2 = #value2 OUT;
PRINT #value1 + ' ' + #value2;
SET NOCOUNT OFF;
Declare #v1 varchar(max), #v2 varchar(200);
Declare #sql nvarchar(max);
Set #sql = N'SELECT #v1 = value1, #v2 = value2
FROM dbo.TblTest -- always use schema
WHERE ID = 61;';
EXEC sp_executesql #sql,
N'#v1 varchar(max) output, #v2 varchar(200) output',
#v1 output, #v2 output;
You should also pass your input, like wherever 61 comes from, as proper parameters (but you won't be able to pass table and column names that way).
Here is a simple example :
Create or alter PROCEDURE getPersonCountByLastName (
#lastName varchar(20),
#count int OUTPUT
)
As
Begin
select #count = count(personSid) from Person where lastName like #lastName
End;
Execute below statements in one batch (by selecting all)
1. Declare #count int
2. Exec getPersonCountByLastName kumar, #count output
3. Select #count
When i tried to execute statements 1,2,3 individually, I had the same error.
But when executed them all at one time, it worked fine.
The reason is that SQL executes declare, exec statements in different sessions.
Open to further corrections.
This will occur in SQL Server as well if you don't run all of the statements at once. If you are highlighting a set of statements and executing the following:
DECLARE #LoopVar INT
SET #LoopVar = (SELECT COUNT(*) FROM SomeTable)
And then try to highlight another set of statements such as:
PRINT 'LoopVar is: ' + CONVERT(NVARCHAR(255), #LoopVar)
You will receive this error.
-- CREATE OR ALTER PROCEDURE
ALTER PROCEDURE out (
#age INT,
#salary INT OUTPUT)
AS
BEGIN
SELECT #salary = (SELECT SALARY FROM new_testing where AGE = #age ORDER BY AGE OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);
END
-----------------DECLARE THE OUTPUT VARIABLE---------------------------------
DECLARE #test INT
---------------------THEN EXECUTE THE QUERY---------------------------------
EXECUTE out 25 , #salary = #test OUTPUT
print #test
-------------------same output obtain without procedure-------------------------------------------
SELECT * FROM new_testing where AGE = 25 ORDER BY AGE OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY

Get column value from dynamically achieved column name

Below is the code where I am getting a columns name dynamically(example F8 is the column name) and storing it in #whcode. What i need is the value that is stored in this column for the where condition specified below. I can see the value stored in the column is 2 but I cannot get it. What it is returning me is the column name itself. How can I get the value in the column. Please help.
declare #count1 int
set #count1 = (select min(srno) from TEMP_STOCK_uPDATE)
declare #whcode varchar(20)
select * from TEMP_STOCK_uPDATE where srno='16091'
set #whcode=(SELECT COLUMN_NAME
FROM Ecata_New.INFORMATION_SCHEMA.COLUMNS
where Table_Name = 'TEMP_STOCK_uPDATE'
and COLUMN_NAME =(select whcode from dbo.temp_stock_map where func=(select func from dbo.temp_stock_map where sr_no=6)))
--select #whcode as 'abcd'
select #whcode as 'abc'
from TEMP_STOCK_uPDATE
where
F1=(select F1 from dbo.TEMP_STOCK_uPDATE where srno=#count1)
You can build your SQL statement dynamically to do this. Build your statement in a varchar and then EXEC it.
DECLARE #SQL VARCHAR(4000)
SET #SQL = 'select ' + #whcode + ' from TEMP_STOCK_uPDATE where F1=(select F1 from dbo.TEMP_STOCK_uPDATE where srno=#count1)'
EXEC (#SQL)

SQL Server 2005 Weird varchar Behavior

This SQL Server 2005 T-SQL code:
DECLARE #Test1 varchar;
SET #Test1 = 'dog';
DECLARE #Test2 varchar(10);
SET #Test2 = 'cat';
SELECT #Test1 AS Result1, #Test2 AS Result2;
produces:
Result1 = d
Result2 = cat
I would expect either
The assignment SET #Test1 =
'dog'; to fail because there isn't
enough room in #Test1
Or the SELECT to return 'dog' in the Result1 column.
What is up with #Test1? Could someone please explain this behavior?
Let me answer with some quotes from the SQL Server documentation.
char and varchar
varchar[(n)]
...
When n is not specified in a data definition or variable declaration statement, the default length is 1.
Converting Character Data
When character expressions are converted to a character data type of a different size, values that are too long for the new data type are truncated.
So, your varchar is declared as a varchar(1), and the implicit conversion in your SET statement (from a string literal of length 3 to a varchar(1)) truncates dog to d.
the varchar is defaulting to length one
DECLARE #Test1 varchar;
try this, which will uses a simple function that takes a sql_variant and returns the data type info back:
CREATE FUNCTION [dbo].[yourFunction]
(
#InputStr sql_variant --can not be varchar(max) or nvarchar(max)
)
returns
varchar(8000)
BEGIN
DECLARE #Value varchar(50)
--can use SQL_VARIANT_PROPERTY(#InputStr,'BaseType') to determine given datatype
--do whatever you want with #inputStr here
IF #InputStr IS NULL
BEGIN
SET #value= 'was null'
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='varchar'
BEGIN
--your special code here
SET #value= 'varchar('+CONVERT(varchar(10),SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='datetime'
BEGIN
--your special code here
SET #value= 'datetime - '+CONVERT(char(23),#InputStr,121)
END
ELSE IF SQL_VARIANT_PROPERTY(#InputStr,'BaseType')='nvarchar'
BEGIN
--your special code here
SET #value= 'nvarchar('+CONVERT(varchar(10),CONVERT(int,SQL_VARIANT_PROPERTY(#InputStr,'MaxLength '))/2)+') - '+CONVERT(varchar(8000),#InputStr)
END
ELSE
BEGIN
--your special code here
set #value= 'unknown!'
END
RETURN #value
END
GO
DECLARE #Test1 varchar;
SET #Test1 = 'dog';
DECLARE #Test2 varchar(10);
SET #Test2 = 'cat';
SELECT #Test1 AS Result1, #Test2 AS Result2;
select [dbo].[yourFunction](#test1)
output:
Result1 Result2
------- ----------
d cat
(1 row(s) affected)
-------------------
varchar(1) - d
(1 row(s) affected)
moral of the story, don't be lazy, specify a length on all of your varchar values!!!

Resources