cakephp condition using '?' in the string [duplicate] - postgresql

For detecting the existence of a key in a hstore, I need to run a query like this:
SELECT * FROM tbl WHERE hst ? 'foo'
However, that gives me a PDOException:
PDOException: SQLSTATE[HY093]: Invalid parameter number: no parameters were bound: SELECT * FROM tbl WHERE hst ? 'foo'
Is there any way to escape the question mark so PDO won't pick it up as a placeholder? I've tried with up to four backslashes, as well as a double question mark (??), but nothing seems to persuade PDO to leave the question mark alone.

Use the function call form. According to the system catalogs, the hstore ? operator uses the exist function:
regress=# select oprname, oprcode from pg_operator where oprname = '?';
oprname | oprcode
---------+---------
? | exist
(1 row)
so you can write:
SELECT * FROM tbl WHERE exist(hst,'foo');
(Personally I'm not a big fan of hstore's operator-centric design and documentation, I think it discards the useful self-documenting properties of a function based interface without any real benefit and I usually use its function calls rather than its operators. Just because you can define operators doesn't mean you should.)

I had the same problem when searching on JSONB data. The full question is here
SELECT * FROM post WHERE locations ? :location;
The workaround on PostgreSQL 9.5 is similar:
SELECT * FROM post WHERE jsonb_exists(locations, :location);
I also opened a ticket at PHP bug tracing system
Update
As Diabl0 mentioned, the proposed solution work but does not use the index.
Tested with:
CREATE INDEX tempidxgin ON post USING GIN (locations);

I suggest you disable PDO native prepared statement so question marks will be ignored:
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true);

Related

What is the TABLE keyword for in this CTE expression

in this I post came across this weird postgres query:
WITH max_i AS
( SELECT MAX(i) FROM test )
SELECT *
FROM test
WHERE i = (TABLE max_i) - 1 ;
It was able to use the CTE return value like a simple variable through the use of the TABLE keyword. I looked through the documentation of postgres, but I couldn't find any mention of what it is or what it is used for. Can someone explain to me how it works?
It's a short way of writing
select * from max_i
See the manual for details

Alphanumeric sorting without any pattern on the strings [duplicate]

I've got a Postgres ORDER BY issue with the following table:
em_code name
EM001 AAA
EM999 BBB
EM1000 CCC
To insert a new record to the table,
I select the last record with SELECT * FROM employees ORDER BY em_code DESC
Strip alphabets from em_code usiging reg exp and store in ec_alpha
Cast the remating part to integer ec_num
Increment by one ec_num++
Pad with sufficient zeors and prefix ec_alpha again
When em_code reaches EM1000, the above algorithm fails.
First step will return EM999 instead EM1000 and it will again generate EM1000 as new em_code, breaking the unique key constraint.
Any idea how to select EM1000?
Since Postgres 9.6, it is possible to specify a collation which will sort columns with numbers naturally.
https://www.postgresql.org/docs/10/collation.html
-- First create a collation with numeric sorting
CREATE COLLATION numeric (provider = icu, locale = 'en#colNumeric=yes');
-- Alter table to use the collation
ALTER TABLE "employees" ALTER COLUMN "em_code" type TEXT COLLATE numeric;
Now just query as you would otherwise.
SELECT * FROM employees ORDER BY em_code
On my data, I get results in this order (note that it also sorts foreign numerals):
Value
0
0001
001
1
06
6
13
۱۳
14
One approach you can take is to create a naturalsort function for this. Here's an example, written by Postgres legend RhodiumToad.
create or replace function naturalsort(text)
returns bytea language sql immutable strict as $f$
select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'),'\x00')
from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r;
$f$;
Source: http://www.rhodiumtoad.org.uk/junk/naturalsort.sql
To use it simply call the function in your order by:
SELECT * FROM employees ORDER BY naturalsort(em_code) DESC
The reason is that the string sorts alphabetically (instead of numerically like you would want it) and 1 sorts before 9.
You could solve it like this:
SELECT * FROM employees
ORDER BY substring(em_code, 3)::int DESC;
It would be more efficient to drop the redundant 'EM' from your em_code - if you can - and save an integer number to begin with.
Answer to question in comment
To strip any and all non-digits from a string:
SELECT regexp_replace(em_code, E'\\D','','g')
FROM employees;
\D is the regular expression class-shorthand for "non-digits".
'g' as 4th parameter is the "globally" switch to apply the replacement to every occurrence in the string, not just the first.
After replacing every non-digit with the empty string, only digits remain.
This always comes up in questions and in my own development and I finally tired of tricky ways of doing this. I finally broke down and implemented it as a PostgreSQL extension:
https://github.com/Bjond/pg_natural_sort_order
It's free to use, MIT license.
Basically it just normalizes the numerics (zero pre-pending numerics) within strings such that you can create an index column for full-speed sorting au naturel. The readme explains.
The advantage is you can have a trigger do the work and not your application code. It will be calculated at machine-speed on the PostgreSQL server and migrations adding columns become simple and fast.
you can use just this line
"ORDER BY length(substring(em_code FROM '[0-9]+')), em_code"
I wrote about this in detail in this related question:
Humanized or natural number sorting of mixed word-and-number strings
(I'm posting this answer as a useful cross-reference only, so it's community wiki).
I came up with something slightly different.
The basic idea is to create an array of tuples (integer, string) and then order by these. The magic number 2147483647 is int32_max, used so that strings are sorted after numbers.
ORDER BY ARRAY(
SELECT ROW(
CAST(COALESCE(NULLIF(match[1], ''), '2147483647') AS INTEGER),
match[2]
)
FROM REGEXP_MATCHES(col_to_sort_by, '(\d*)|(\D*)', 'g')
AS match
)
I thought about another way of doing this that uses less db storage than padding and saves time than calculating on the fly.
https://stackoverflow.com/a/47522040/935122
I've also put it on GitHub
https://github.com/ccsalway/dbNaturalSort
The following solution is a combination of various ideas presented in another question, as well as some ideas from the classic solution:
create function natsort(s text) returns text immutable language sql as $$
select string_agg(r[1] || E'\x01' || lpad(r[2], 20, '0'), '')
from regexp_matches(s, '(\D*)(\d*)', 'g') r;
$$;
The design goals of this function were simplicity and pure string operations (no custom types and no arrays), so it can easily be used as a drop-in solution, and is trivial to be indexed over.
Note: If you expect numbers with more than 20 digits, you'll have to replace the hard-coded maximum length 20 in the function with a suitable larger length. Note that this will directly affect the length of the resulting strings, so don't make that value larger than needed.

Replacement of DBA_Source in Postgres to find DB objects

How can I know table (table_1) is being used in which all UDF?
Below query gives me table's details:
SELECT * FROM information_schema.tables;
Below Query gives UDF details:
select * FROM pg_proc;
But how can I know that table1 is used in which all UDF?
A string search in the prosrc column of pg_proc is the only way if you want to find dependencies between functions and tables.
Of course that is not very satisfying, because it would be rather difficult to say if – say – an occurrence of table_1 is a reference to the table or a variable name. Also, you cannot find the source of C functions in the catalog.
To get a reliable answer, you would need insight into the language in which the function is written, and here is the core of the problem: PostgreSQL does not have any insight into the language! PostgreSQL's fabled extensibility allows you to define new languages for functions, and only the language handler knows how to interpret the string that is the function body. That also holds for PL/pgSQL which is shipped with PostgreSQL.
That is also the reason why there are no pg_depend entries for objects used in functions.

Postgres Concatenation

I'm trying to a simple concatenation in PostgreSQL and it keeps up throwing up an error message. I don't understand what I am doing wrong here.
select concat('abcde', 'fgh');
No function matches the given name and argument types. You might need to add explicit type casts.
select concat(cast('abcde' as text), cast('fgh' as text));
No function matches the given name and argument types. You might need to add explicit type casts.
I am using Postgres version 8.4.11. Please let me know what is going on.
The concat operator is ||, so select 'abcde' || 'fgh' should work. Also, as #jonathan.cone suggested, check out the docs.
concat was added in 9.1, it doesn't exist in 8.4. As others have noted, use the || operator.
Compare the 8.4 docs to the 9.1 docs and you'll notice that the concat function isn't present in the 8.4 docs.
See that bar at the top of the docs that says "This page in other versions" ? It's really handy when you're working with an old version, or if you find a link to an old version of a page via Google and you're on a newer version. Always make sure you're looking at the docs for the right version.
It'd be nice if the tables for functions etc included a "First appeared in version " - but unfortunately they don't.
If you're ever confused about the availablilty of a function, you can use \df in psql to list and search functions.
For example, to list all functions named concat use \df concat
For all functions in the pg_catalog schema (built-in functions) use \df pg_catalog. - note the trailing period, telling psql you mean "any function within the schema pg_catalog" not "the function named pg_catalog".
For all functions with names starting with concat use \df concat*
It's a very powerful tool. The same language works when searching for schema (\dn), tables (\dt), etc.
select 'abcde' || 'fgh';
select cast('abcde' as text) || cast('fgh' as text);

Is it possible to use CASE with IN?

I'm trying to construct a T-SQL statement with a WHERE clause determined by an input parameter. Something like:
SELECT * FROM table
WHERE id IN
CASE WHEN #param THEN
(1,2,4,5,8)
ELSE
(9,7,3)
END
I've tried all combination of moving the IN, CASE etc around that I can think of. Is this (or something like it) possible?
try this:
SELECT * FROM table
WHERE (#param='??' AND id IN (1,2,4,5,8))
OR (#param!='??' AND id in (9,7,3))
this will have a problem using an index.
The key with a dynamic search conditions is to make sure an index is used, instead of how can I easily reuse code, eliminate duplications in a query, or try to do everything with the same query. Here is a very comprehensive article on how to handle this topic:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
It covers all the issues and methods of trying to write queries with multiple optional search conditions. This main thing you need to be concerned with is not the duplication of code, but the use of an index. If your query fails to use an index, it will preform poorly. There are several techniques that can be used, which may or may not allow an index to be used.
here is the table of contents:
Introduction
The Case Study: Searching Orders
The Northgale Database
Dynamic SQL
Introduction
Using sp_executesql
Using the CLR
Using EXEC()
When Caching Is Not Really What You Want
Static SQL
Introduction
x = #x OR #x IS NULL
Using IF statements
Umachandar's Bag of Tricks
Using Temp Tables
x = #x AND #x IS NOT NULL
Handling Complex Conditions
Hybrid Solutions – Using both Static and Dynamic SQL
Using Views
Using Inline Table Functions
Conclusion
Feedback and Acknowledgements
Revision History
if you are on the proper version of SQL Server 2008, there is an additional technique that can be used, see: Dynamic Search Conditions in T-SQL Version for SQL 2008 (SP1 CU5 and later)
If you are on that proper release of SQL Server 2008, you can just add OPTION (RECOMPILE) to the query and the local variable's value at run time is used for the optimizations.
Consider this, OPTION (RECOMPILE) will take this code (where no index can be used with this mess of ORs):
WHERE
(#search1 IS NULL or Column1=#Search1)
AND (#search2 IS NULL or Column2=#Search2)
AND (#search3 IS NULL or Column3=#Search3)
and optimize it at run time to be (provided that only #Search2 was passed in with a value):
WHERE
Column2=#Search2
and an index can be used (if you have one defined on Column2)
if #param = 'whatever'
select * from tbl where id in (1,2,4,5,8)
else
select * from tbl where id in (9,7,3)