IN clause for multiple columns on the same value set - postgresql

I have a WHERE clause like below:
WHERE t1.col1 IN ('a', 'b', 'c') OR t2.col2 IN ('a', 'b', 'c');
I know that the two IN clauses will always have the same contents. Is there any way I can avoid duplicating them?
Something like:
WHERE (t1.col1 OR t2.col2) IN ('a', 'b', 'c')

You can use the array overlaps operator:
where array[t1.col1, t2.col2] && array['a','b','c']

with flt(x) as (values('{a,b,c}'::text[]))
select
...
from ..., flt
where t1.col1 = any(flt.x) or t2.col2 = any(flt.x);
There are two ways to use some constant values in the PostgreSQL. Using CTE as shown above. Or using "options":
set foo.bar to '{a,b,c}';
select '{c,d,e}'::text[] && current_setting('foo.bar')::text[];

Related

Postgres: How Can I DRY up Multiple WHERE IN (a,b,c,...) Clauses?

In a programming language like Javascript, instead of doing:
['a', 'b', 'c', 'd'].map(...);
['a', 'b', 'c', 'd'].filter(...);
['a', 'b', 'c', 'd'].forEach(...);
I can do the following:
const names = ['a', 'b', 'c', 'd'];
names.map(...);
names.filter(...);
names.forEach(...);
If I have several SQL statements in a file:
SELECT * FROM foo WHERE something IN ('a', 'b', 'c', 'd');
SELECT * FROM bar WHERE something_else IN ('a', 'b', 'c', 'd');
SELECT * FROM baz WHERE another_thing IN ('a', 'b', 'c', 'd')
Is there a similar way I can "create an array variable" and then use it repeatedly in all those queries? I know things get complicated because ('a', 'b', 'c', 'd') isn't actually an array, and I'm not sure if I should be using a literal variable, a view, or a function to hold the ('a', 'b', 'c', 'd') part.
The closest analogy would be a temporary table.
CREATE TEMP TABLE targets (t text);
COPY targets FROM stdin;
a
b
c
d
...thousands more rows
\.
SELECT foo.* FROM foo JOIN targets ON foo.x = targets.t
However, it is less common in a database to need to match one set of values against multiple tables because that can imply your database structure needs reworking.

How can I create many tables of the main one and store them?

my_table = readtable('some.csv')
'L1' 'B'
'L2' 'B'
'L3' 'A'
'L4' 'C'
'L5' 'B'
'L6' 'C'
'L7' 'C'
'L8' 'A'
In the second column there are different values, B, A, and C. I want to create smaller tables from this one, depending on the value in the right, so the expected outcome would be:
'L1' 'B'
'L2' 'B'
'L5' 'B'
and
'L3' 'A'
'L8' 'A'
and
'L4' 'C'
'L6' 'C'
'L7' 'C'
each of them stored in a different variable for later use.
You can access table contents with my_table.Variables or my_table.my_column_header. From there, you can compare the values with A, B and C; then create new tables.
% Get the ABC column.
content=my_table.Variables;
ABC = char(content{:,2});
% Alternatively, if you have the header.
ABC = char(my_table.Line2);
% Create new tables.
tableA = table(my_table(ABC == 'A',:));
tableB = table(my_table(ABC == 'B',:));
tableC = table(my_table(ABC == 'C',:));

Interconnecting tables on PostgreSQL

I am a newbie here.
I am using PostgreSQL to manipulate lots of data in my specific field of research. Unfortunately, I am encountering a problem that is not allowing me to continue my analysis. I tried to simplify my problem to clearly illustrate it.
Let's suppose I have a table called "Buyers" with those data:
table_buyers
The buyers can make ONLY ONE purchase in each store or none. There are three stores and there a table for each one. Just like below:
table_store1
table_store2
table_store3
To create the tables, I am using the following code:
CREATE TABLE public.buyer
(
ID integer NOT NULL PRIMARY KEY,
name text NOT NULL,
phone text NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store1
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store2
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store3
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
To add the information on the tables, I am using the following code:
INSERT INTO buyer (ID, name, phone) VALUES
(1, 'Alex', 88888888),
(2, 'Igor', 77777777),
(3, 'Mike', 66666666);
INSERT INTO Store1 (ID_buyer, total_order, total_itens) VALUES
(1, 87.45, 8),
(2, 14.00, 3),
(3, 12.40, 4);
INSERT INTO Store2 (ID_buyer, total_order, total_itens) VALUES
(1, 785.12, 7),
(2, 9874.21, 25);
INSERT INTO Store3 (ID_buyer, total_order, total_itens) VALUES
(2, 45.87, 1);
As all the tables are interconnected by buyer's ID, I wish I could have a query that generates an output just like this:
desired output table.
Please, note that if the buyer did not buy anything in a store, I must print '0'.
I know this is an easy task, but unfortunately, I have been failing on accomplish it.
Using the 'AND' logical operator, I tried the following code to accomplish this task:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer AND
buyer.id = store2.id_buyer AND
buyer.id = store3.id_buyer;
But, obviously, it just returned 'Igor' as this was the only buyer that have bought items on all three stores (print screen).
Then, I tried the 'OR' logical operator, just like the following code:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer OR
buyer.id = store2.id_buyer OR
buyer.id = store3.id_buyer;
But then, it returns 12 lines with wrong values (print screen).
Clearly, my mistake is about not considering that 'Buyers' don't have to on all three stores on my code. I just can't correct it on my own, can you please help me?
I appreciate a lot for an answer that can light up my way. Thanks a lot!
Tips about how I can search for this issue are very welcome as well!
Ok. I doubt that this is the final answer for you, but its a start
SELECT
buyer.id,
buyer.name,
COALESCE( gb_store1.total_orders, 0 ) as store1_total,
COALESCE( gb_store2.total_orders, 0 ) as store2_total,
COALESCE( gb_store3.total_orders, 0 ) as store3_total
FROM
public.buyer,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store1
GROUP BY ID_buyer ) gb_store1 ON gb_store1.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store2
GROUP BY ID_buyer ) gb_store2 ON gb_store2.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store3
GROUP BY ID_buyer ) gb_store3 ON gb_store3.id_buyer = buyer.id ;
So, this query has a couple elements should focus on. The subselects/groupby allow you to total within your subtables by ID_buyer. The LEFT OUTER JOIN make its so your query can still return a result, even if a subselect finds no matching record. Finally, the COALESCE allows you to return 0 when one of your totals is NULL (because the subselect found no match).
Hope this helps.

Zend Select with multiple bound parameters

I'm trying to write a query using a Zend_Db_Table object with multiple bound parameters. My query, before I put in the real values, was being run as follows:
$mytbl = new MyTable();
$myresults = $mytbl->fetchAll(
"(col1 LIKE 'val1' AND col2 LIKE 'val2')
OR (col1 LIKE 'val2' AND col2 LIKE 'val1'))
AND col3 = 'val3');
This works - except my variables are not being quoted properly. I tried to then replace the second line with:
$mytbl->fetchAll(
"(col1 LIKE ? AND col2 LIKE ?)
OR (col1 LIKE ? AND col2 LIKE ?))
AND col3 = ?,
array(
$db->quote($val1),
$db->quote($val2),
$db->quote($val2),
$db->quote($val1),
$db->quote($val3)));
but then I get an SQL error that the number of parameters does not match. So I tried:
$mytbl->fetchAll(
$mytbl->select()
->where("(col1 LIKE ? AND col2 LIKE ?)
OR (col1 LIKE ? AND col2 LIKE ?)) AND col3 = ?,
array(
$db->quote($val1),
$db->quote($val2),
$db->quote($val2),
$db->quote($val1),
$db->quote($val3))));
but I get the same error. If I put a single parameter, without the array, then it does the replacement properly, but with the array, it does not work. How can I bind multiple parameters like this?
just use IN and put the questionmark in brackets... example:
$productIds = array(1, 2, 3);
$select = $db->select()
->from('products',
array('product_id', 'product_name', 'price'))
->where('product_id IN (?)', $productIds);
You don't need to quote your variables, ZF takes care of this for you:
$mytbl->fetchAll(
"(col1 LIKE ? AND col2 LIKE ?)
OR (col1 LIKE ? AND col2 LIKE ?))
AND col3 = ?",
array($val1, $val2, $val3, $val4, $val5));

Simplifying PostgreSQL Full Text Search tsvector and tsquery with aliases

I am trying to simplify this query as it is going to be dynmaically generated by PHP and I would like to reduce the processing overhead (the real query will be much longer but the structure will be the same!).
SELECT title, type_name, ts_rank_cd(ARRAY[0.1,0.2,0.4,1.0],
setweight(to_tsvector(coalesce(title,'')), 'A')
||
setweight(to_tsvector(coalesce(type_name,'')), 'B')
,
to_tsquery('search & query'))
FROM TestView WHERE
setweight(to_tsvector(coalesce(title,'')), 'D')
||
setweight(to_tsvector(coalesce(type_name,'')), 'B')
##
to_tsquery('search & query');
I am looking to try to reduce the need to specify the tsquery and tsvector twice by defining something like an alias so that it does not have to be specified twice. Something like this (which fails, I am not sure if it is even close to correct!)
SELECT title, type_name, ts_rank_cd(ARRAY[0.1,0.2,0.4,1.0],
searchvector
,
searchquery
FROM TestView WHERE
setweight(to_tsvector(coalesce(title,'')), 'D')
||
setweight(to_tsvector(coalesce(type_name,'')), 'B') AS searchvector
##
to_tsquery('search & query') AS searchquery;
Is this possible or am I just stuck with generating it all twice.
For context 'TestView' is a view generated from a number of tables.
Any help much appreciated!
SELECT title,
type_name,
ts_rank_cd(ARRAY[0.1,0.2,0.4,1.0],weight,query)
FROM (
SELECT title,
type_name,
setweight(to_tsvector(coalesce(title,'')), 'A')
||setweight(to_tsvector(coalesce(type_name,'')), 'B') as weight,
to_tsquery('search & query') as query
FROM TestView
) t
WHERE weight ## query