I'm trying to do a select from a table based on the value of a name field.
I want to only match certain criteria if a record is not based on the first, ie.
if the following matches:
WHERE name='version'
if so return that single row, if not, look for these too:
WHERE name='v' OR name='e' OR name='r'
etc...
Is this possible in a single query?
Many thanks!
SELECT ...
FROM ..
WHERE name = 'version'
UNION ALL
SELECT ...
FROM ...
WHERE NOT EXISTS (
SELECT ...
FROM ..
WHERE name = 'version'
) AND name IN ('v', 'e', 'r', ...)
EDIT:
Not sure how to test if mysql would cache the results of the first query, but assuming you run the query in a new session:
SELECT #cached:=1, ...
FROM ..
WHERE name = 'version'
UNION ALL
SELECT 2, ...
FROM ...
WHERE #cached IS NULL AND name IN ('v', 'e', 'r', ...)
should work
If in the same session you want to clear the #cached with
SELECT #cached:=1, ...
FROM .., (SELECT #cached:=0) x
WHERE name = 'version'
UNION ALL
SELECT 2, ...
FROM ...
WHERE #cached = 0 AND name IN ('v', 'e', 'r', ...)
select ...
from ..
where (name = 'version') or (name in ('v', 'e', 'r', ...))
Though, since you're basing the comparison on the same field, there's no difference between the above version and
...
where (name in ('version', 'v', 'e', 'r', ...))
Related
Postgres (12.2) Setup:
CREATE TABLE public.test_table (
id int NOT NULL,
value_type text NOT NULL,
value text NOT NULL
);
INSERT INTO public.test_table
(id, value_type, value)
VALUES (1, 'string', 'a'),
(2, 'json', '{"hello":"world"}'),
(3, 'json', '{"color":"blue"}');
Initial Queries:
select value::jsonb as json_value from test_table where value_type = 'json'
json_value |
------------------|
{"hello": "world"}|
{"color": "blue"} |
But I'm only interested in ones with 'color'.
Moving it to a subquery so that I can get only 'color', also just fine:
select only_json.json_value
from(
select value::jsonb as json_value from test_table where value_type = 'json'
) only_json
where only_json.json_value ? 'color' = true
json_value |
------------------|
{"color": "blue"} |
Now let's break that main table up into two, and suddenly effectively the same query has trouble:
CREATE TABLE public.test_table (
id INT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE public.test_types (
id INT PRIMARY KEY REFERENCES public.test_table (id),
value_type TEXT NOT NULL
);
INSERT INTO public.test_table
(id, value)
VALUES (1, 'a'),
(2, '{"hello":"world"}'),
(3, '{"color":"blue"}');
insert into public.test_types
(id, value_type)
values (1, 'string'),
(2, 'json'),
(3, 'json');
Now this query:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
returns, as expected:
id|value |
--|------------------|
2|{"hello": "world"}|
3|{"color": "blue"} |
But as soon as I attach the where clause, it fails:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
where only_json.value ? 'color' = true
SQL Error [22P02]: ERROR: invalid input syntax for type json
Detail: Token "a" is invalid.
Where: JSON data, line 1: a
It's somehow resurrected the value of 'a' that was well-eliminated prior to this where clause. So what gives? Why does the join cause it to apply the last where clause (which should happen logically last) too early? Failed workarounds I've tried:
Using left join instead of natural join.
Applying where value_type = 'json' to the joined table first, prior to the join.
Moving it to a "with".
Creating a view and then applying the where clause to a select from the view.
Creating a column via select called is_color_holder with SELECT only_json.value ? 'color' as is_color_holder. This column populates correctly, but if I use a where clause, WHERE is_color_holder = true, I receive the same error.
Repeating the value_type='json' expression in the problematic where clause.
Moving the cast up a subquery.
Replacing the join with where id in (select id from public.test_types where value_type = 'json')
Comma-style joins.
Centering the query around the types table first, then joining the value type after the types have already been filtered.
Is this a bug I should report to postgres? Am I missing something?
Edit: I managed one workaround. See my answer for more details. Still looking for a better answer, though.
select id, value from (
select id, case when value_type = 'json' then value::jsonb else to_jsonb(value) end as value, value_type from
public.test_table natural join public.test_types
where value_type = 'json') as_json
where value ? 'color' = true
id|value |
--|-----------------|
3|{"color": "blue"}|
I suspect that what you are seeing is premature optimization, caused by predicate pushdown.
In Postgres, a common strategy to avoid that is the offset 0 hack:
select id, value from (
select id, value
from public.test_table
inner join public.test_types using(id)
where value_type = 'json'
offset 0 -- (try to) prevent predicate pushdown
) only_json
where value::jsonb ? 'color'
Demo on DB Fiddle
I found a workaround. I'll post as an 'answer' here and edit the above question.
But if anyone has a better answer for me, I'll make yours as the correct one.
Casting non-json values to json with "CASE" works fine:
select id, value from (
select id, case when value_type = 'json' then value::jsonb else to_jsonb(value) end as value, value_type from
public.test_table natural join public.test_types
where value_type = 'json') as_json
where value ? 'color' = true
id|value |
--|-----------------|
3|{"color": "blue"}|
I am trying to use this query, but when there are the same value in different columns, I receive this error:
1060 - Duplicate column name "123"
For example here:
INSERT INTO chiro(in_out,chirocov,chirocov2,chiroded,chiromet,
chirocovp,chirooop,chirooopmet,chirooopcp,
chirovisit,chirouse,chiromax,chirodedapply,
chironum1,chironum2)
SELECT * FROM (SELECT 'in', 'no','individual','123','123','20',
'213','21243','10','14','5','2000','yes',
'0','1') AS tmp
WHERE NOT EXISTS (SELECT in_out,chirocov,chirocov2,
chiroded,chiromet,chirocovp,chirooop,
chirooopmet,chirooopcp, chirovisit,chirouse,
chiromax,chirodedapply,chironum1,chironum2
FROM chiro
WHERE chirocov='no'
AND chirocov2='individual'
AND chiroded='123'
AND chiromet='123'
AND chirocovp='20'
AND chirooop='213'
AND chirooopmet='213'
AND chirooopcp='10'
AND chirovisit='14'
AND chirouse='5'
AND chiromax='2000'
AND chirodedapply='yes'
AND chironum1='0'
AND chironum2='1')
LIMIT 1
But when i change the value, there won"t be any errors. like:
INSERT INTO chiro(in_out,chirocov,chirocov2,chiroded,
chiromet,chirocovp,chirooop,chirooopmet,
chirooopcp, chirovisit,chirouse,chiromax,
chirodedapply,chironum1,chironum2)
SELECT * FROM (SELECT 'in', 'no','individual','123','231','20',
'213','21243','10','14','5','2000',
'yes', '0','1') AS tmp
WHERE NOT EXISTS (SELECT in_out,chirocov,chirocov2,
chiroded,chiromet,chirocovp,chirooop,
chirooopmet,chirooopcp, chirovisit,chirouse,
chiromax,chirodedapply,chironum1,chironum2
FROM chiro
WHERE chirocov='no'
AND chirocov2='individual'
AND chiroded='123'
AND chiromet='123'
AND chirocovp='20'
AND chirooop='213'
AND chirooopmet='213'
AND chirooopcp='10'
AND chirovisit='14'
AND chirouse='5'
AND chiromax='2000'
AND chirodedapply='yes'
AND chironum1='0'
AND chironum2='1')
LIMIT 1
Could you help me and let me know what I am doing wrong?
Just remove the outer SELECT from:
SELECT * FROM (SELECT 'in', 'no','individual','123','123','20',
'213','21243','10','14','5','2000','yes',
'0','1') AS tmp
because it retrieves all the unnamed columns from the inner select with names that are their values, so there are 2 columns with the same name 123.
See a simplified demo of the problem.
Use this:
INSERT INTO chiro(
in_out, chirocov, chirocov2, chiroded, chiromet, chirocovp, chirooop, chirooopmet, chirooopcp,
chirovisit, chirouse, chiromax, chirodedapply, chironum1, chironum2
)
SELECT 'in', 'no', 'individual', '123', '123', '20', '213', '21243', '10', '14', '5', '2000', 'yes', '0', '1'
WHERE NOT EXISTS(
SELECT in_out, chirocov, chirocov2, chiroded, chiromet, chirocovp, chirooop, chirooopmet, chirooopcp,
chirovisit, chirouse, chiromax, chirodedapply, chironum1, chironum2
FROM chiro
WHERE chirocov='no' AND chirocov2='individual' AND chiroded='123' AND chiromet='123' AND chirocovp='20'
AND chirooop='213' AND chirooopmet='213' AND chirooopcp='10' AND chirovisit='14' AND chirouse='5'
AND chiromax='2000' AND chirodedapply='yes' AND chironum1='0' AND chironum2='1'
)
Also LIMIT 1 is not necessary.
I am trying to insert values coming from a select and variable :
INSERT INTO routeur (`codeAdherent`, `quantiteArticle`, `dateFin`) VALUES
(SELECT `codeAdherent` FROM adherents WHERE categorie = 'G', `quantiteArticle` = $a, `dateFin`= $b);
Write it with and without VALUES, with and without IN, with and without brackets but I always get an synthax error.
Where is my mistake?
Try below:
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin)
SELECT codeAdherent, #a, #b FROM adherents WHERE categorie = 'G'
You have to read carefully the INSERT syntax because you have got many errors.
This is the right syntax:
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin)
SELECT codeAdherent, '$a', '$b'
FROM adherents
WHERE categorie = 'G'
PS: To avoid the SQL Injection you should use Prepared Statements
You can try this out :
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin) VALUES
(SELECT codeAdherent FROM adherents WHERE categorie = 'G', $a, $b);
I have a table with Boolean values (0 and 1 only) that needs to be CSV-ed to a client. I know I can do 1 replace like this:
SELECT REPLACE(email, '%40', '#'),
REPLACE(name,'%20', ' '),
REPLACE(icon_clicked, 1, 'Yes')
FROM myTable
WHERE id > 1000;
This will convert all the values of 1 to 'Yes', but how to do this in a single query for both 1 => Yes and 0 => No so Boolean result is stored in a single column? I tried to do this:
SELECT REPLACE(email, '%40', '#'),
REPLACE(name,'%20', ' '),
REPLACE(icon_clicked, 1, 'Yes'),
REPLACE(icon_clicked, 0, 'No')
FROM myTable
WHERE id > 1000;
But this query created an additional column for the 'No' string replace (so final result had 4 columns, email, name, icon_clicked->yes, icon_clicked->no)
One way is to nest REPLACE:
SELECT REPLACE(REPLACE(icon_clicked, 0, 'No'), 1, 'Yes')), ...
FROM myTable
...
or use CASE WHEN (this will work for most RDBMS comparing to IF function which is MySQL related):
SELECT CASE WHEN icon_clicked THEN 'Yes' ELSE 'No' END, ...
FROM myTable
...
SqlFiddleDemo
EDIT:
There is also one nice way utilizing ELT:
SELECT icon_clicked,
ELT(FIELD(icon_clicked,0,1),'No','Yes'),
ELT(icon_clicked + 1, 'No', 'Yes')
FROM mytable
SqlFiddleDemo2
No need to use nested Replace or Case statement. Try using IF, which is way simpler
SELECT
icon_clicked,
IF(icon_clicked,'Yes','No')
FROM myTable
SQL FIDDLE DEMO
I've a User Table which has "Access Role" Defined in the table itself.
Schema:
UserID, UserName, Access_1, Access_2, Access_3, .... Access_10
Here values for Access_Columns is 1 or 0
Objective: I need Users and their Allowed accesses in one column as 'Access_Allowed'.
UserId, Access_Allowed
1, (Access_1, Access_3, Access_4, ...)
2, (Access_2, Access_3, Access_5, Access_10, ...)
Regards,
Yugal
Thanks Strawberry... I've Used Concat_ws here...
select userid, username,
concat_ws(', ',
IF(Access1 = 1 ,'Access1', NULL),
IF(Access2 = 1 ,'Access2', NULL),
IF(Access3 = 1 ,'Access3', NULL),
IF(Access4 = 1 ,'Access4', NULL),
.
.
IF( AccessN = 1 ,'AccessN',NULL) as Access_Allowed
from UserTable
Thanks