How to get length of column with name "text"?
This doesn't work:
SELECT LEN([text]) AS mylength FROM mytable
Error is:
Argument data type text is invalid for argument 1 of len function.
Use DATALENGTH . LEN() cannot be used on columns of type 'TEXT'
SELECT DATALENGTH([text]) AS mylength FROM mytable
BTW: text is a very poor choice of name for a column. Also, the TEXT datatype has been deprecated. Use varchar(max) instead (or nvarchar(max) or varbinary(max))
Related
I have a simple function that has optional parameters. When I leave out a parameter, which should just default to null, I get an error that it is not an integer.
Here is the function:
CREATE FUNCTION rewrite(_postid integer DEFAULT NULL::integer,
_url character varying DEFAULT NULL::character varying)
RETURNS TABLE
(
PostTypeID integer,
DestinationURL varchar,
)
LANGUAGE plpgsql
AS
$function$
BEGIN
RETURN QUERY
SELECT
NULL AS PostTypeID,
_url AS DestinationURL,
FROM reference.destinations dest1
WHERE length(TRIM(dest1.DestinationURL)) > 0
AND _url LIKE '%' || TRIM(dest1.DestinationURL)) || '%'
ORDER BY length(dest1.DestinationURL)) DESC
LIMIT 1;
END;
$function$
If I run SELECT * FROM utility.rewrite(_url := 'wikipedia.org') then I get this error:
[42804] ERROR: structure of query does not match function result type
Detail: Returned type text does not match expected type integer in
column 1.
So column1 must be the PostTypeID column in my RETURNS TABLE definition. But I am selecting NULL AS PostTypeID so why is it not just returning NULL?
If I run SELECT * FROM utility.rewrite(_postid = 0, _url := 'wikipedia.org') then it works fine. But I don't want 0 to be returned, I want NULL.
Just because you use the alias posttypeid in the query does not mean that PostgreSQL infers the data type of your PL/pgSQL variable.
Even though NULL can be any data type, PostgreSQL has to determine a data type for the result column of the query. Lacking other information, it arbitrarily chooses text.
Mapping the query result type to the function result type happens later, in PL/pgSQL. That is what causes the error you observe.
You can avoid the problem by specifying the type of NULL with an explicit type cast:
SELECT CAST (NULL AS integer)
I want to calculate the average number from a column in PostgreSQL
SELECT AVG(col_name)
From TableName
It gives me this error:
ERROR: function avg (character varying) does not exist
LINE 1: SELECT AVG(col_name)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Store numbers in a numeric field, an integer, decimal or whatever. But not in a text/varchar field.
Check the manual for all numeric data types.
Nasty workaound: CAST some records to a numeric value and keep others as text. Example:
/*
create temp table foo AS
SELECT x FROM (VALUES('x'),('1'),('2')) sub(x);
*/
WITH cte AS (
SELECT
CASE
WHEN x ~ '[0-9]' THEN CAST(x AS decimal) -- cast to numeric field
END AS num,
CASE
WHEN x ~ '[a-zA-Z]' THEN x
END AS a
FROM foo
)
SELECT AVG(num), COUNT(a) FROM cte;
We have fields with varying lengths and want to right-pad them with spaces to the field length defined in the schema.
The following statement is working:
SELECT RPAD(field, LENGTH(field), ' ') AS field FROM schema.table;
This produces an SQL error 206 with SQLState 42703: is not valid in the context where it is used.
// Our application resolves the prepared statement's ? - this is working fine
INSERT INTO schema.table (field) VALUES (RPAD(?, LENGTH(field), ' '));
The same happens with:
INSERT INTO schema.table (field) VALUES (RPAD(?, LENGTH(schema.table.field), ' '));
Is there any possibility to avoid hardcoding the field length?
Your problem is that scalar functions operate on rows; LENGTH(field) only works within a statement that returns rows, such as a select statement. To understand why, imagine putting some other function in place of LENGTH(). LCASE(field), for example, takes the lowercase of the string in a particular row. It wouldn't make sense applied generically to a column. Even LENGTH() can vary row-by-row in some cases: if the column is of type VARCHAR, LENGTH() returns the length of the actual string.
The solution is to select any row, perform the LENGTH() operation on the field, and store the result in a variable:
CREATE OR REPLACE VARIABLE field_length INTEGER;
SET field_length = (
SELECT LENGTH(field) FROM schema.table
WHERE field IS NOT NULL
FETCH FIRST ROW ONLY
);
You only need to do this once in your code. Then, whenever you need to use the length:
INSERT INTO schema.table (field) VALUES (RPAD(?, field_length, ' '));
Note that this solution depends on field being defined as a CHAR(x) rather than a VARCHAR(x). If you had to do this with a VARCHAR, you could find out the length of the field from the syscat.columns system table.
EDIT: added handling of null values since LENGTH() could return null if the value in field is null.
If you want a fixed length column, why are you using VARCHAR? Use CHAR - DB2 will automatically pad the values for you.
I want to calculate the average number from a column in PostgreSQL
SELECT AVG(col_name)
From TableName
It gives me this error:
ERROR: function avg (character varying) does not exist
LINE 1: SELECT AVG(col_name)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Store numbers in a numeric field, an integer, decimal or whatever. But not in a text/varchar field.
Check the manual for all numeric data types.
Nasty workaound: CAST some records to a numeric value and keep others as text. Example:
/*
create temp table foo AS
SELECT x FROM (VALUES('x'),('1'),('2')) sub(x);
*/
WITH cte AS (
SELECT
CASE
WHEN x ~ '[0-9]' THEN CAST(x AS decimal) -- cast to numeric field
END AS num,
CASE
WHEN x ~ '[a-zA-Z]' THEN x
END AS a
FROM foo
)
SELECT AVG(num), COUNT(a) FROM cte;
Pointedly what I'm asking below is: What is the actual data type of the #cleartext parameter of this SQL function? >> ENCRYPTBYKEY (..) -
http://msdn.microsoft.com/en-us/library/ms174361.aspx
(If you read below this line you can follow the history and reasoning. I think it's trickier than it first appears.)
The SQL Server documentation states the #cleartext (2nd) parameter of the EncryptByKey(..) function can accept a number of various types:
EncryptByKey (#key_GUID , #cleartext [, #add_authenticator, #authenticator] )
#cleartext Is a variable of type
nvarchar, char, varchar, binary,
varbinary, or nchar that contains data
that is to be encrypted with the key.
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - - - - - But what is its actual declared data type? ...
If I were to create a custom function (totally separate from the EncryptByKey example given above) what actual data type do I give a custom parameter so it will accept all those same types in the same manner?
Edit 1: I'm actually wrapping the SQL EncryptByKey function in a custom
UDF function and I want to recreate the same
parameter types to pass through to
it. This is the reasoning behind my want to create exact same params by type.
Edit 2: If I try using sql_variant it results in the error
Msg 8116, Level 16, State 1, Procedure
EncryptWrapper, Line 17 Argument data
type sql_variant is invalid for
argument 2 of EncryptByKey function.
Edit 3:
Here's my custom wrapper function - and the direct problem. What should the data type of #cleartext be for direct pass through to EncryptByKey?
ALTER FUNCTION [dbo].[EncryptWrapper]
(
#key_GUID uniqueidentifier,
#cleartext -- ??????????? <<< WHAT TYPE ????????????????
#add_authenticator int = 0,
#authenticator sysname = NULL
)
RETURNS varbinary(8000)
AS
BEGIN
-- //Calling a SQL Server builtin function.
-- //Second param #cleartext is the problem. What data type should it be?
Return EncryptByKey(#key_GUID, #cleartext, #add_authenticator, #authenticator)
END
Note: I shouldn't have to use CAST or CONVERT - I only need to use the proper data type for my #cleartext param.
Edit 4: Discovered the EncryptByKey(..) #cleartext parameter is not the following types:
sql_variant- raises error when passed
varbinary- too restrictive- doesn't allow passing of the text types otherwise accepted by EncryptByKey(..)
sysname, nvarchar, varchar- weird behaviour- tends to take only the first character of the argument text or something
try sql_variant:
CREATE FUNCTION [dbo].[yourFunction]
(
#InputStr sql_variant --can not be varchar(max) or nvarchar(max)
)
returns
varchar(8000)
BEGIN
--can use SQL_VARIANT_PROPERTY(#InputStr,'BaseType') to determine given datatype
--do whatever you want with #inputStr here
RETURN CONVERT(varchar(8000),#InputStr) --key is to convert the sql_varient to something you can use
END
GO
the key is to convert the sql_varient to something you can use within the function. you can use IF statements and check the BaseType and convert the sql_varient back into the native data type
EDIT
here is an example of how to get the original datatype:
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')='char'
BEGIN
--your special code here
SET #value= 'char('+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
test it out:
DECLARE #x char(5), #z int, #d datetime, #n nvarchar(27)
SELECT #x='abc',#d=GETDATE(),#n='wow!'
select [dbo].[yourFunction](#x)
select [dbo].[yourFunction](#d)
select [dbo].[yourFunction](#z)
select [dbo].[yourFunction](#n)
test output:
-------------------------------------
char(5) - abc
(1 row(s) affected)
-------------------------------------
datetime - 2010-02-17 15:10:44.017
(1 row(s) affected)
-------------------------------------
was null
(1 row(s) affected)
-------------------------------------
nvarchar(27) - wow!
(1 row(s) affected)
ENCRYPTBYKEY() almost certainly isn't written in vanilla T-SQL. It doesn't need to follow T-SQL data typing rules.
That said, if you want to write a wrapper for it, use SQL_VARIANT for the #cleartext parameter, just as KM suggested.
If ENCRYPTBYKEY() is not sensitive to the max length of #cleartext, you could munge all CHAR/VARCHARs to VARCHAR(8000), and all NCHAR/NVARCHARs to NVACHAR(4000).
Otherwise you may be SOL: any data type conversion that respects maximum length--eg, CHAR(10) vs CHAR(20)--will require dynamic SQL, so you would have to write it as a stored procedure, rather than a function. At that point, it's not really a wrapper anymore.