Postgres concatenate regex and table column - sql

I would like to run the query
SELECT * FROM table_a JOIN table_b ON table_a.title LIKE ('^[a-b]' || table_b.title)
where if table_a.title = "the foo bar", and table_b.title has a row named "oo", the query will not return any results, but if table_b.title has a row named "foo", it will return results.
Basically I want to match the title on table_b only if it is an entire word (surrounded by spaces, or at the beginning and end of string) but not if another word has a part of table_b.title in it.
Any ideas?

If you want to use regular expressions in Postgres, use ~, similar to, or regexp_matches, not like:
SELECT *
FROM table_a JOIN
table_b
ON table_a.title ~ ('^[a-b]' || table_b.title);

This is what ended up working for me:
SELECT 1 FROM table_a JOIN table_b
ON (' ' || table_b.title || ' ') ~* ('.*?[^a-zA-Z0-9]+' || table_b.name || '[^a-zA-Z0-9].*?')
where table_b.id IS NOT NULL

Related

Dynamic column fields using existing column values in SQL

I have this existing query
Select
mt.First_name,
mt.Last_name as OLD_Last_name,
ot.Last_name as New_Last_name,
ot.Date as Update_Date,
from maintable as mt
JOIN othertable as ot on mt.id=ot.id
I'd like to join a new column with the following output:
[mt.First_name] [ot.Last_name], nee [mt.Last_name] changed their name on [ot.Date].
I tried using a case statement but didn't get it right.
For closure, moving #Jnevill answer from comment to actual answer:
SELECT mt.First_name || ' ' || ot.Last_name || ', nee ' || mt.last_name || ' changed their name on ' || ot.Date AS yournewcolumn, mt.First_name
, mt.Last_name as OLD_Last_name
, ot.Last_name as New_Last_name
, ot.Date as Update_Date
from maintable as mt
JOIN othertable as ot on mt.id=ot.id
Apparently OP wanted to know how to concatenate strings, which is done with ||.

PostgreSQL - join tables using pattern matching

I have two tables and need to join them using two columns that are similar.
The first table is called articles has a column called 'slug' with slug lines for articles, ex: 'trump-fails-yet-again.'
The second table is called log and has a column called path with the url path for the articles, ex: '/articles/trump-fails-yet-again/'
Here is my search query:
"SELECT articles.title, count(*) as num FROM articles, log WHERE articles.slug LIKE CONCAT('%',log.path) GROUP BY articles.title;"
This returns nothing but brackets, []
I have also tried:
"SELECT articles.title, count(*) as num FROM articles JOIN log ON articles.slug SIMILAR TO CONCAT('%',log.path) GROUP BY articles.title;"
That returns a DataError: invalid regular expression: quantifier operand invalid
Any help is greatly appreciated!
try this:
` select articles.title, count(*) as views from articles
join log on articles.slug ~~ ('%' || articles.slug || '%')
group by articles.title;`
You have a slash at the end of the path. How about this?
SELECT a.title, count(*) as num
FROM articles a JOIN
log l
ON a.path LIKE '%' || l.slug || '%'
GROUP BY a.title;
You should also learn to use proper, explicit JOIN syntax. Never use commas in the FROM clause.
Because there is a 1:1 function with this you can do this
SELECT articles.title, count(*) as num
FROM articles
JOIN log ON articles.slug = '/articles/' || articles.slug || '/'
GROUP BY articles.title;
Or even better
CREATE FUNCTION slug_to_article_path( slug text )
RETURNS text AS
$$
SELECT '/articles/' || slug || '/';
$$ LANGUAGE sql
IMMUTABLE;
SELECT articles.title, count(*) as num
FROM articles
JOIN log ON articles.slug = slug_to_article_path(articles.slug)
GROUP BY articles.title;

How to replace NULL value in select with subquery

Hi I'm performing a left join on two tables. If a particular column is NULL I want to run a subquery to get a value from a completely different table. Here's what I have now:
SELECT A.ACCOUNT_NUM, A.USER_ID,
CASE B.PREFERRED_NAME
WHEN '' THEN RTRIM(B.FIRST_NAME) || ' ' || B.LAST_NAME
ELSE RTRIM(B.PREFERRED_NAME) || ' ' || B.LAST_NAME
END AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
TABLE_B sometimes doesn't contain a record that matches with TABLE_A, so I want to run a subquery from TABLE_C that contains usernames and will match on A.USER_ID.
I thought I could do something like:
CASE B.PREFERRED_NAME
WHEN NULL THEN subquery here
But I get this error:
ERROR [42703] [IBM][DB2] SQL0206N "NULL" is not valid in the context where it is used.
Probably because NULLs are not allowed for that column.
SOLVED
Thanks for the help. This is how I solved my issue:
SELECT A.ACCOUNT_NUM, A.USER_ID,
CASE
WHEN B.PREFERRED_NAME IS NULL THEN C.USER_ID
WHEN B.PREFERRED_NAME IS NOT NULL THEN
CASE PREFERRED_NAME
WHEN '' THEN RTRIM(B.FIRST_NAME) || ' ' || B.LAST_NAME
ELSE RTRIM(B.PREFERRED_NAME) || ' ' || B.LAST_NAME
END
END AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
JOIN TABLE_C C
ON A.USER_ID = C.USER_ID
Depending on your query, you can probably just add your third table as another LEFT JOIN, then add the column you want to a COALESCE function:
Also, it looks like you're storing the preferred name as spaces if there isn't one, in which case you can use the NULLIF function to convert it to a NULL, which will work with your COALESCE.
Here's an example of what I mean:
SELECT
A.ACCOUNT_NUM
,A.USER_ID
,COALESCE(
NULLIF(B.PREFERRED_NAME,'')
,B.FIRST_NAME
,C.OTHER_NAME
) || ' ' || B.LAST_NAME AS NAME
FROM TABLE_A A
LEFT JOIN TABLE_C C
ON C.USER_ID = A.USER_ID
LEFT JOIN TABLE_B B
ON A.USER_ID = B.USER_ID
If you know there is always going to be a row in C that matches A, then you could convert that to a regular (inner) JOIN.
The reason you're getting the error, though is because you can't use NULL like that in a CASE statement. If you want to have a NULL case, then you have to do it like #Abecee said in the comment with CASE WHEN B.PREFERRED_NAME IS NULL THEN ...

Understanding a basic SQL query

I have a query like
SELECT tran_number
FROM table_a WHERE customer_id IN
(SELECT customer_id
FROM table_b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)
The above code is working fine. The concern is with the inner query (copied inner query alone below)...
(SELECT customer_id
FROM table_b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
When i execute this query, i'm getting error as customer_id: invalid identifier. In real, table_b is not having any field named customer_id. If so, then how it is working, without any issue, when i use it as an inner query above.
Please help me to understand this.
Database details below
Oracle 11G Enterprise edition 11.2.0.2.0
PL/SQL Release 11.2.0.2.0
if the where condition of that inner select has a result, then the column customer_id from table_a will be selected.
If not then it won't be selected. The outer select checks that with the in condition. That is like saying: "Only return something if the inner select return true."
It's a matter of scope. Oracle validates identifiers starting with the innermost sub-query and working outwards. If we add table aliases to your original query things might become clearer:
SELECT t1.tran_number
FROM table_a t1
WHERE t1.customer_id IN
(SELECT t1.customer_id
FROM table_b t2
WHERE t2.customer_key = 89564
AND ( t2.other_phn_area_code
|| t2.other_phnum_pfx_num
|| t2.other_phnum_sfx_num IN
(123456789)))
AND t1.phn_area_code || t1.phnum_pfx_num || t1.phnum_sfx_num IN (123456789)
In effect, the outer query is using the sub-querty as a test for EXISTS, i.e. just checking for the existence of a given value of CUSTOMER_KEY and those other columns. If this is not what you want then you should change the column name in the sub-query. (And that's a pretty good bet: you're probably getting puzzling results from the main query and that's why you're investigating the sub-query in isolation).
Using aliases in these scenarios is always good practice. If you had aliased the sub-query like this:
....
WHERE t1.customer_id IN
(SELECT t2.customer_id
FROM table_b t2
WHERE t2.customer_key = 89564
....
the error would have been immediately apparent.
The SQL Reference does explain the operation of scope in sub-queries, but it's hard to find. What it says is this:
"Oracle resolves unqualified columns in the subquery by looking in
the tables named in the subquery and then in the tables named in the
parent statement"
You can find a clearer explanation of scoping in the PL/SQL documentation; SQL sub-queries work in the same fashion. Find out more.
Thats is a known bug with IN. If you use table alias you will get error
SELECT tran_number
FROM table_a WHERE customer_id IN
(SELECT b.customer_id
FROM table_b b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789)))
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)
Also use EXISTS to avoid this type of silent behaviour
SELECT tran_number
FROM table_a as t1 WHERE EXISTS
(SELECT *
FROM table_b as b
WHERE customer_key = 89564
AND ( other_phn_area_code
|| other_phnum_pfx_num
|| other_phnum_sfx_num IN
(123456789))
AND b.customer_id =t1.customer_id)
AND phn_area_code || phnum_pfx_num || phnum_sfx_num IN (123456789)

Find all tables with a field containing xml string values

I have an SQL 2005 database and I know that in the database there is a table which has got some xml strings in it. How can I find this table(s)?
If the fields are actually of type XML, then this query will give you what you're looking for:
select * from information_schema.columns
where DATA_TYPE = 'XML'
Marc
Run this:
select 'select distinct ''' || a.name || '.' || b.name
|| ''' from ' || b.name
|| 'where ' || b.name || ' like ''%<%/>%'' union '
from systable a
join syscolumns b on (a.id = b.id)
join systypes c on (b.type = c.xtype)
where a.type ='U' and c.name = ('CHAR', 'CHARN', 'VARCHAR', 'VARCHARN');
The first result set will have one row per character column in the database:
select distinct 'table.column' from table where column like '%<%/>%' union
Take that resultset, snip off the last union, and run the resultset as a SQL statement. It'll bring back the table name and column name for any column that has one or more rows that look XML-ish.
Edit: this is from memory; the join to systypes and the type names may be wrong, so select from systypes and check.