Check for integer in string array - arrays

I am trying to check a string array for existence of a converted integer number. This sits inside of a procedure where:
nc_ecosite is an integer variable
current_consite is a string array
ecosite is an integer
current_ecosite_nc is double
IF to_char(nc_ecosite, '999') IN
(select current_consite from current_site_record
where current_ecosite_nc::integer = nc_ecosite) THEN
ecosite := nc_ecosite;
The result always comes from the ELSIF that follows the first IF. This occurs when nc_ecosite is in the array (from checks). Why is ecosite not being populated with nc_ecosite when values are matching?
I am working with Postgres 9.3 inside pgAdmin.

I found the following to provide the desired result:
IF nc_ecosite in
(select (unnest(string_to_array(current_consite, ',')))::integer
from current_site_record
where current_ecosite_nc::integer = nc_ecosite) THEN
ecosite := nc_ecosite::integer;

The immediate reason for the problem is that to_char() inserts a leading blank for your given pattern (legacy reasons - to make space for a potential negative sign). Use the FM Template Pattern Modifier to avoid that:
to_char(nc_ecosite, 'FM999')
Of course, it would be best to operate with matching data types to begin with - if at all possible.
Barring that, I suggest this faster and cleaner statement:
SELECT INTO ecosite nc_ecosite -- variable or column??
WHERE EXISTS (
SELECT 1 FROM current_site_record c
WHERE current_ecosite_nc::integer = nc_ecosite
AND to_char(nc_ecosite, 'FM999') = ANY(current_consite)
);
IF NOT FOUND THEN ... -- to replace your ELSIF
Make sure you don't run into naming conflicts between parameters, variables and column names! A widespread convention is to prepend variable names with _ (and never use the same for column names). But you better table-qualify column names in all queries anyway. You did not make clear which is a column and which is a variable ...
I might be able to optimize the statement further if I had the complete function and table definition.
Related:
Remove blank-padding from to_char() output
Variables for identifiers inside IF EXISTS in a plpgsql function
Naming conflict between function parameter and result of JOIN with USING clause

Related

How to use special characters in SQL Server variables?

I'm trying to declare a variable like #wi-fi in SQL Server but dash character in not allowed. There are other characters like / * - ( ) with same problem. I know for table or column name we can put it in the [] but what about declaring variables? is there a solution?
I have already searched the web but couldn't find any way.
Local variable names must comply with the rules for identifiers. MSDN says that :
When identifiers are used in Transact-SQL statements, the identifiers that do not comply with these rules must be delimited by double quotation marks or brackets.
But this rule is not valid for variables naming. In SQL Server there is a restriction on variable names. All variable names must begin with a single # sign. After that the variable name must follow the rules for identifiers and can contain a total of 128 characters.When we say characters, we mean that the name can contain letters, numbers, the # sign, the pound sign, the dollar sign and the underscore character. The variable name can not contain any dash or spaces.
READ MORE HERE
Some context to answer people such as #Alex who need to know the "why" for questions like this:
I ran into a similar problem, I needed to use just a small piece of a URL saved in my db where the front and ends were irrelevant.
I first attempted to use:
declare #variable varchar(250) = %x%;
SELECT * FROM tblone WHERE column1 LIKE '#variable'
however this returned error: Arithmetic overflow error converting numeric to data type varchar
My working query was formatted:
declare #variable varchar(1000) = x;
SELECT * FROM tblone WHERE column1 LIKE '%'+#variable+'%'

Find rows where text array contains value similar to input

I'm trying to get rows where a column of type text[] contains a value similar to some user input.
What I've thought and done so far is to use the 'ANY' and 'LIKE' operator like this:
select * from someTable where '%someInput%' LIKE ANY(someColum);
But it doesn't work. The query returns the same values as that this query:
select * from someTable where 'someInput' = ANY(someColum);
I've got good a result using the unnest() function in a subquery but I need to query this in WHERE clause if possible.
Why doesn't the LIKE operator work with the ANY operator and I don't get any errors? I thought that one reason should be that ANY operator is in the right-hand of query, but ...
Is there any solution to this without using unnest() and if it is possible in WHERE clause?
It's also important to understand that ANY is not an operator but an SQL construct that can only be used to the right of an operator. More:
How to use ANY instead of IN in a WHERE clause with Rails?
The LIKE operator - or more precisely: expression, that is rewritten with to the ~~ operator in Postgres internally - expects the value to the left and the pattern to the right. There is no COMMUTATOR for this operator (like there is for the simple equality operator =) so Postgres cannot flip operands around.
Your attempt:
select * from someTable where '%someInput%' LIKE ANY(someColum);
has flipped left and right operand so '%someInput%' is the value and elements of the array column someColum are taken to be patterns (which is not what you want).
It would have to be ANY(someColum) LIKE '%someInput%' - except that's not possible with the ANY construct which is only allowed to the right of an operator. You are hitting a road block here.
Related:
Is there a way to usefully index a text column containing regex patterns?
Can PostgreSQL index array columns?
You can normalize your relational design and save elements of the array in separate rows in a separate table. Barring that, unnest() is the solution, as you already found yourself. But while you are only interested in the existence of at least one matching element, an EXISTS subquery will be most efficient while avoiding duplicates in the result - Postgres can stop the search as soon as the first match is found:
SELECT *
FROM tbl
WHERE EXISTS (
SELECT -- can be empty
FROM unnest(someColum) elem
WHERE elem LIKE '%someInput%'
);
You may want to escape special character in someInput. See:
Escape function for regular expression or LIKE patterns
Careful with the negation (NOT LIKE ALL (...)) when NULL can be involved:
Check if NULL exists in Postgres array
An admittedly imperfect possibility might be to use ARRAY_TO_STRING, then use LIKE against the result. For example:
SELECT *
FROM someTable
WHERE ARRAY_TO_STRING(someColum, '||') LIKE '%someInput%';
This approach is potentially problematic, though, because someone could search over two array elements if they discover the joining character sequence. For example, an array of {'Hi','Mom'}, connected with || would return a result if the user had entered i||M in place of someInput. Instead, the expectation would probably be that there would be no result in that case since neither Hi nor Mom individually contain the i||M sequence of characters.
My question was marked duplicate and linked to a question out of context by a careless mod. This question comes closest to what I asked so I leave my answer here. (I think it may help people for who unnest() would be a solution)
In my case a combination of DISTINCT and unnest() was the solution:
SELECT DISTINCT ON (id_) *
FROM (
SELECT unnest(tags) tag, *
FROM someTable
) x
WHERE (tag like '%someInput%');
unnest(tags) expands the text array to a list of rows and DISTINCT ON (id_) removes the duplicates that result from the expansion, based on a unique id_ column.
Update
Another way to do this without DISTINCT within the WHERE clause would be:
SELECT *
FROM someTable
WHERE (
0 < (
SELECT COUNT(*)
FROM unnest(tags) AS tag
WHERE tag LIKE '%someInput%'
)
);
Please check this out.
This answer was exactly what I was looking for. It also provides for some useful tips (and examples) in case you need more flexibility.
It basically explains the ANY(), the #> and the && operators.
"If you want to search multiple values, you can use #> operator"
"#> means contains all the values in that array. If you want to search if the current array contains any values in another array, you can use &&"

Oracle PL/SQL: Approach to select an array of ids first, then loop/process them

I, Oracle newbie, am trying to select the primary key ids with a complicated query into an array structure to work with this afterwards.
The basic workflow is like:
1. Select many ids (of type long) found by a long and complicated query (if I would know if and how this is possible, I would separate this into a function-like subquery of its own)
2. Store the ids in an array like structure
3. Select the rows with those ids
4. Check for duplicates (compare certain fields for equality)
5. exclude some (i.e. duplicates with a later DATE field)
I read a lot of advice on PL / SQL but haven't found the right concept. The oracle documentation states that if I need an array, I should use VARRAY.
My best approach so far is
declare
TYPE my_ids IS VARRAY(100000) OF LONG
begin
SELECT id INTO my_ids FROM myTable WHERE mycondition=true
-- Work with the ids here, i.e. loop through them
dbms_output.put_line('Hello World!');
END;
But I get an error: "Not suitable for left side". Additionally, I don't want to declare the size of the array at the top.
So I think this approach is wrong.
Could anyone show me a better one? It doesn't have to be complete code, just "use this data structure, these SQL-structures, that loop and you'll get what you need". I think I could figure it out once I know which direction I should take.
Thanks in advance!
My_ids in your example is a type, not a variable.
You cannot store data into the type, you can store data only to some variable of this type.
Try this code:
declare
TYPE my_ids_type IS VARRAY(100000) OF LONG; /* type declaration */
my_ids my_ids_type; /* variable declaration */
begin
SELECT my_id BULK COLLECT INTO my_ids FROM myTable;
-- Work with the ids here, i.e. loop through them
dbms_output.put_line('Hello World!');
END;
Read this article: http://www.oracle.com/technetwork/issue-archive/2012/12-sep/o52plsql-1709862.html
to learn basics how to bulk collect data in PL/SQL.

How do I cast on an update? The data field is set to vchar(4) and I would like to cast it as an int

This is what I have come up with so far but I am getting a syntax error
update t_Provider set(CAST(f_Postnr as int)) = '66886' where f_Name= 'Test1'
By your question, I assume column f_Postnr is a NVARCHAR(4).
You cannot insert data into this column with a greater character length then 4.
Any solution would require you to edit the column length.
Please look at the syntax for an update in MSDN..
The left hand side of the assignment has to be a known memory location (variable or column/field). What you are doing is trying to set and expression (unknown location) to a constant and filter by a where clause.
Use this code to do what you want.
-- Code from user
update t_Provider
set f_Postnr = 66886
where f_Name= 'Test1'
Why not pass the constant in the update without stating it as a string ''? That's what the above snippet does. This assumes f_Postnr is a INT which can easily handle a 66K number (domain constraint).
If you use '66886', SQL Server will implicit cast the string to an integer if there are no errors.
Here is a huge chart for MSDN on implicit casting matrix.
I hope this helps with your future TSQL endeavors!
What about using...
UPDATE t_Provider
SET f_Postnr = CAST('66886' AS INT)
WHERE f_Name = 'Test1'

Is there any C SQLite API for quoting/escaping the name of a table?

It's impossible to sqlite3_bind_text a table name because sqlite3_prepare_v2 fails to prepare a statement such as:
SELECT * FROM ? ;
I presume the table name is needed to parse the statement, so the quoting needs to have happened before sqlite3_prepare_v2.
Is there something like a sqlite3_quote_tablename? Maybe it already exists under a name I can't recognize, but I can't find anything in the functions list.
SQLite will escape identifiers for you with the %w format in the https://www.sqlite.org/printf.html family of functions.
your proposed sqlite3_quote_tablename function could sanitize the input to prevent sql injection attacks. To do this it could parse the input to make sure it is a string literal. http://sqlite.org/lang_expr.html#litvalue
If a table name has invalid characters in it you can enclose the table name in double quotes, like this.
sqlite> create table "test table" (id);
sqlite> insert into "test table" values (1);
sqlite> select * from "test table";
id
----------
1
Of course you should avoid using invalid characters whenever possible. It complicates development and is almost always unnecessary (IMO the only time it is necessary is when you inherit a project that is already done this way and it's too big to change).
When using SQLite prepared statements with parameters the parameter: "specifies a placeholder in the expression for a literal value that is filled in at runtime"
Before executing any SQL statement, SQLite "compiles" the SQL string into a series of opcodes that are executed by an internal Virtual Machine. The table names and column names upon which the SQL statement operates are a necessary part of the compilation process.
You can use parameters to bind "values" to prepared statements like this:
SELECT * FROM FOO WHERE name=?;
And then call sqlite3_bind_text() to bind the string gavinbeatty to the already compiled statement. However, this architecture means that you cannot use parameters like this:
SELECT * FROM ? WHERE name=?; // Can't bind table name as a parameter
SELECT * FROM FOO WHERE ?=10; // Can't bind column name as a parameter
If SQLite doesn't accept table names as parameters, I don't think there is a solution for your problem...
Take into account that:
Parameters that are not assigned values using sqlite3_bind() are treated as NULL.
so in the case of your query, the table name would be NULL which of course is invalid.
I was looking for something like this too and couldn't find it either. In my case, the expected table names were always among a fixed set of tables (so those were easy to validate). The field names on the other hand weren't so I ended up filtering the string, pretty much removing everything that was not a letter, number, or underscore (I knew my fields would fit this parameters). That did the trick.

Resources