Why text value is not equal to char value? - postgresql

I am looking through a book about PostgreSQL and find the following example:
SELECT
'a '::VARCHAR(2) = 'a '::TEXT AS "Text and varchar",
'a '::CHAR(2) = 'a '::TEXT AS "Char and text",
'a '::CHAR(2) = 'a '::VARCHAR(2) AS "Char and varchar";
which yields:
which is strange and in the book is said:
The preceding example shows that 'a '::CHAR(2) equals 'a
'::VARCHAR(2), but both have different lengths, which isn't logical.
Also, it shows that 'a'::CHAR(2) isn't equal to 'a '::text. Finally,
'a '::VARCHAR(2) equals 'a'::text. The preceding example causes
confusion because if variable a is equal to b, and b is equal to
c, a should be equal to c according to mathematics.
But there is no explanation why? Is there something about how data is stored or maybe this is some legacy behavior of the CHAR type?

The behavior you are seeing appears to be explained by that Postgres is doing some casting behind the scenes. Consider the following slightly modified version of your query:
SELECT
'a '::VARCHAR(2) = 'a '::TEXT AS "Text and varchar",
'a '::CHAR(2) = CAST('a '::TEXT AS CHAR(2)) AS "Char and text",
'a '::CHAR(2) = 'a '::VARCHAR(2) AS "Char and varchar";
This returns True for all three comparisons. Apparently Postgres is casting both sides of the second comparison to text, and a[ ] ([ ] indicates a space) is not the same thing coming from a CHAR(2) and from text.
Recall that in order to do an A = B comparison in a SQL database, both types of A and B have to be the same. If not, even if it appears that the equality comparison be working on its own, under the hood most likely there is an implicit cast happening.

Related

Why does tsquery in postgres exclude the letter 't'?

When I run the following statement in postgres it removes the letter 't' from the resulting tsquery:
Statement: select websearch_to_tsquery('english', 'xtp t');
Result: xtp
Same result with underscore separating:
Statement: select websearch_to_tsquery('english', 'xtp_t');
Result: xtp
However, if I substitute the 't' for any other letter I get the expected result:
Statement: select websearch_to_tsquery('english', 'xtp_l');
Result: 'xtp' <-> 'l'
Why does the letter 't' get removed from the result of the original query?
Not any other letter. i, a, s, and t are considered stop words in English. 'I' and 'a' actually are very common words, s and t are presumably included because they occur in contractions and possessives, like "I can't stand dog's slobbery toys"

What is PostgreSQL syntax for complex array literals? (e.g. circle[])

The PostgreSQL docs state that, though some literals have special syntax, generic syntax for literal values looks like type 'data', 'data'::type, or CAST('data' AS type). For instance, one could write the integer 16 as 16 or as '16'::int. Dollar-quoting is also allowed, so $$16$$:int works as well.
For some types, such as circle, the generic syntax is (as far as I can tell) the only way to write a literal. Four syntaxes are listed for circle: <(x, y), r>, ((x, y), r), (x, y), r, and x, y, r; however, none of these seem to work plain:
y=> create table t (c circle);
CREATE TABLE
y=> insert into t (c) values ( <(1,2),3> );
ERROR: syntax error at or near "<"
LINE 1: insert into t (c) values ( <(1,2),3> );
^
y=> insert into t (c) values ( ((1,2),3) );
ERROR: column "c" is of type circle but expression is of type record
LINE 1: insert into t (c) values ( ((1,2),3) );
^
HINT: You will need to rewrite or cast the expression.
y=> insert into t (c) values ( (1,2),3 );
ERROR: INSERT has more expressions than target columns
LINE 1: insert into t (c) values ( (1,2),3 );
^
y=> insert into t (c) values ( 1,2,3 );
ERROR: INSERT has more expressions than target columns
LINE 1: insert into t (c) values ( 1,2,3 );
^
On the contrary, using the generic syntax works fine:
y=> insert into t (c) values ( '1,2,3' );
INSERT 0 1
y=> select c from t;
c
-----------
<(1,2),3>
Also documented is the array syntax, which is comma-delimited and curly-enclosed. So the range 1-5 may be written, for instance, as '{1, 2, 3, 4, 5}'::int[].
Unfortunately, the array syntax does not seem to support values specified with the generic syntax: $${ 1 }$$::int[] is accepted, but $${ '1'::int }::int[] is rejected, as is $${ '1' }::int[].
Because array literal syntax does not accept generic-form syntax for elements, and because generic syntax seems to be the only way to write a circle literal, it appears that it is impossible to write a circle literal in PostgreSQL.
y=> select $${ }$$::circle[];
circle
--------
{}
(1 row)
y=> select $${ '<(1, 2), 3>'::circle }$$::circle[];
ERROR: invalid input syntax for type circle: "'<(1"
LINE 1: select $${ '<(1, 2), 3>'::circle }$$::circle[];
Is this indeed the case?
Note: yes, it's crucial that I want to write a circle[] literal. The reason is that my use-case is for specifying values in prepared statements in a PostgreSQL client, and, as the PostgreSQL protocol documentation says (emphasis mine):
Parameter data types can be specified by OID; if not given, the parser attempts to infer the data types in the same way as it would do for untyped literal string constants.
This means that, for instance, ARRAY[ '<(1,2),3>'::circle ] is not a valid solution, as it does not use literal syntax.
Turns out the solution is simple: escape the commas!
y=> select '{ <(1\,2)\,3>, <(4\,5)\,6> }'::circle[];
circle
---------------------------
{"<(1,2),3>","<(4,5),6>"}
(1 row)
Also an option is to double-quote the elements:
y=> select '{ "<(1,2),3>", "<(4,5),6>" }'::circle[];
circle
---------------------------
{"<(1,2),3>","<(4,5),6>"}
(1 row)
Credit to sehrope on Github for telling me this. Quote from the relevant part of the PostgreSQL docs:
[...] when writing an array value you can use double quotes around any individual array element. You must do so if the element value would otherwise confuse the array-value parser. For example, elements containing curly braces, commas [...], double quotes, backslashes, [etc,] must be double-quoted. [...] Alternatively, you can avoid quotes and use backslash-escaping to protect all data characters that would otherwise be taken as array syntax.

Need to count specific letters from a search, why does it show as zero? the data is formatted as ntext

===CLICK ME FOR THE IMAGE OF THE CODE== I need the count to be (3 for example on the first row) but it shows zero, what is wrong with the code, this is ntext which is why it was casted to nvarchar
I am trying to count the occurrence of the letter 'a' on a column named Description, however using the query below I am getting the result of zero.
SELECT Description,
(
datalength(Description) -
datalength(replace(cast(Description as nvarchar(max)), 'a', ''))
)
/datalength(Description) [a]
FROM Categories
An int divided by an int will return an int. Notice the *1.0
Example
Declare #Description nvarchar(max)='Cat in the Hat was never seen'
SELECT #Description, (( datalength(#Description) - datalength(replace(cast(#Description as nvarchar(max)), 'a', '')) )*100) / datalength(#Description) [a]
Returns
(No column name) a
Cat in the Hat was never seen 10

KDB string concatenation with symbol list for dynamic query

In this link, there is an example on how to include a dynamic parameter. d, in a KDB select query:
h: hopen`:myhost01:8012 // open connection
d: 2016.02.15 // define date var
symList: `GBPUSD`EURUSD
h raze "select from MarketDepth where date=", string d, ", sym in `GBPUSD`EURUSD" // run query with parameter d
Here d is of type date and is easy to string concatenate in order to generate a dynamic query.
If I want to add symList as a dynamic parameter as well by converting to string:
raze "select from MarketDepth where date=", string d, ", sym in ", string symList
The concatenated string becomes: select from MarketDepth where date=2016.02.15, sym in GBPUSDEURUSD, in other words the string concatenation loses the backticks so the query does not run. How can I solve this?
p.S: I know about functional querying but after failing for 2 hours, I have given up on that.
No need for functional selects.
q)MarketDepth:([] date:9#2016.02.15; sym:9#`A`B)
q)d:2016.02.15
q)symList:`B
q)h ({[dt;sl] select from MarketDepth where date=dt,sym in sl}; d; symList)
date sym
--------------
2016.02.15 B
2016.02.15 B
2016.02.15 B
2016.02.15 B
You are right, string SYMBOL does not preserve a backtick character, so you'll have to append it yourself like this:
symList: `GBPUSD`EURUSD
strSymList: "`",'string symList / ("`GBPUSD";"`EURUSD")
I used join , with each-both adverb ' to join a backtick with each element of a list. Having your symbol list stringified your dynamic query becomes
"select from MarketDepth where date=", (string d), ", sym in ",raze"`",'string symList
You can also use parse to see how a shape of a functional form of your query will look like.
q) parse "select from MarketDepth where date=", (string d), ", sym in ",raze"`",'string symList
(?;`MarketDepth;enlist ((=;`date;2016.02.15);(in;`sym;enlist `GBPUSD`EURUSD));0b;())
Now it's easy to create a functional select:
?[`MarketDepth;enlist ((=;`date;2016.02.15);(in;`sym;enlist symList));0b;()]
Hope this helps.
Update: #Ryan Hamilton's solution is probably the best in your particular scenario. You can even make a table name an argument if you want:
h({[t;d;s]select from t where date=d,sym in s};`MarketDepth; d; symList)
But it is worth noting that you can't use this technique when you need to make a list of columns dynamic. The following will NOT work:
h({[c;d;s]select c from t where date=d,sym in s};`time`sym; d; symList)
You will have to either build a dynamic select expression like you do or use functional forms.
Others have already given good alternative approaches for your problem. But in case if you need to join string and symbols (or other data types) without losing backtick, function .Q.s1 does the task.
q) .Q.s1 `a`b
q)"`a`b"
q)"select from table where sym in ",.Q.s1 symlist
Note: Generally it is not suggested to use .Q namespace functions.

Where is the syntax error in this TSQL query?

The following TQL query is generated from a tool I'm using but when it's executed there is a syntax error near 'LIKE'. I can't seem to figure out what the problem is. Does anybody know what's wrong?
The error from SQL Management Studio is "Msg 156, Level 15, State 1, Line 17 Incorrect syntax near the keyword 'LIKE'."
SELECT COUNT_BIG(*)
FROM [HistoryReport] AS t0
WHERE (1 <> 0 AND
(CASE WHEN (
(CASE WHEN (t0.[CategoryValue] IS NULL)
THEN NULL
ELSE LOWER(t0.[CategoryValue])
END) IS NULL
)
THEN NULL
ELSE (
(CASE WHEN (t0.[CategoryValue] IS NULL)
THEN NULL
ELSE LOWER(t0.[CategoryValue])
END) LIKE 'U' + '%'
)
END) <> 0)
A few things, It seems very strange that you are testing if a value is null, and returning null if it is and the value if it isn't. Then you're checking for nulls again in the branch of code that is only executed if the value is definitely not null. A bit unnecessary and very confusing. In addition, I suspect your comparison with NULL isn't going to work the way you think it is since NULL <> 0 will evaluate to NULL by default.
As an aside: normally, in SQL SERVER, strings are case-insensitive (unless you configure the column or server with a case-sensitive collation which is somewhat uncommon.)
I'm trying hard to figure out what you actually mean, here is a syntactically correct version of what I think you're trying to do:
SELECT COUNT_BIG(*)
FROM [HistoryReport] AS t0
WHERE (1 <> 0 AND LOWER(t0.categoryValue) like 'U' + '%')
Basically your query states that if t0.categoryValue is null, return null otherwise convert t0.categoryvalue to lowercase and compare, using like, to 'U%' and return true if the LIKE comparison returns true. The query above accomplishes the same thing.
IF you aren't using a case-sensitive collation, you can remove LOWER() since it only adds cost and prevents any index usage.
Now, in SQL Server's world NULL means 'unknown' so asking "Does this unknown value = 0" can only give the answer "I don't know." This confuses a lot of people because they expect "NULL==NULL" which works in some languages, but in SQL Sever you're basically asking "Is this unknown value the same as this other unknown value" and the answer is, again, unknown.
So I guess my only follow-up question, is how to do you want nulls to be treated?
Also, as to your original question, it would appear that expressions like your LIKE 'U' + '%' don't like being in the middle of CASE statements.
BOL states the syntax is:
Simple CASE expression:
CASE input_expression
WHEN when_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
Searched CASE expression:
CASE
WHEN Boolean_expression THEN result_expression [ ...n ]
[ ELSE else_result_expression ]
END
and that:
THEN result_expression Is the expression returned when
input_expression equals when_expression evaluates to TRUE, or
Boolean_expression evaluates to TRUE. result expression is any valid
expression.
And what you have with LIKE seems like it should be a valid expression but it appears that LIKE is on the fringe of valid operatiors.