Get substring from regular expression - everything up to a character - regex

There is a questions table with a column called options which contains
'r>>>>>5####answer1|4####answer2|3####answer3|2####answer4|1####answer5'
Which are pairs of possible answers
5 = answer1
4 = answer2
etc.
The questions are set up by users but the pattern is the same.
Another table has a users answer which is the option selected. I'm attempting to write SQL to extract the answer text.
For example 4 should display answer2.
I tried:
SELECT substring(question.options from '%4#####"%#"[\|]%' for '#') AS answertext
FROM ...
But it displays answer2|3####answer3|2####answer4.
How do I get everything up to the first |?

The regular expression supplied by #Abelisto in a comment works nicely. You don't need to escape | in the character class, so it can be:
SELECT substring(options, '4#+([^|]+)' ...
Assuming the number of the answer is limited to a single digit, there is a simple solution without regular expressions, too:
SELECT right(x, -5) AS answer
FROM unnest( string_to_array(right(
'r>>>>>5####answer1|4####answer2|3####answer3|2####answer4|1####answer5'
, -6), '|')) x
WHERE x LIKE '4%'; -- pick number
Applied to your table:
SELECT right(x, -5) AS answer
FROM question q, unnest( string_to_array(right(q.options, -6), '|')) x
WHERE q.question_id = 1
AND x LIKE '4%';
But really, you should normalize your unfortunate design. It's a 1:n design between question and answers. Assuming a PK question.question_id you can fix it quickly like this:
CREATE TABLE answer AS
SELECT q.question_id, left(x,1)::int AS answer_id, right(x, -5) AS answer
FROM question q, unnest(string_to_array(right(q.options, -6), '|')) x;
ALTER TABLE answer
ADD PRIMARY KEY (question_id, answer_id)
, ADD CONSTRAINT q_fk FOREIGN KEY (question_id) REFERENCES question(question_id)
ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE question DROP column options;
Then your query simply is:
SELECT answer
FROM answer
WHERE question_id = 1
AND answer_id = 4;
Related:
How to implement a many-to-many relationship in PostgreSQL?

Related

Conditionally format cell if value entered already appears in same row, next column which is CSV

I'm want to conditionally format A3:A if the value entered in A3:A already appears in B3:B, which contains CSV, >1 time.
(A3:A will be CONCATENATED to B3:B, so the value will automatically appear at least once.)
Basically, if the value is not already present, there will be no formatting and I know to go ahead and add (leave it). If it is present, format the cell to alert me not to add (or delete). There may be numerous values in some cells and not so easy to glance to see if the value in question is already present.
I attempted to use REGEXMATCH, but not really sure how to switch the TRUE to a numeric value.
=IF(LEN(A3),REGEXMATCH(B3,A3),)
I've also found other formulas using COUNTIF and COUNTA that perform a similar action, but none that consider CSV.
My sheet
custom formula for CF:
=ARRAYFORMULA(REGEXMATCH(A3,TEXTJOIN("|",1,TRANSPOSE(QUERY(QUERY(TRANSPOSE(TRIM(
SPLIT(B3,","))), "select Col1,count(Col1) group by Col1"),
"select Col1 where Col2 > 1", 0)))))

Sqlite- I've got the select, how do I update these records? [duplicate]

This question already has answers here:
How do I make an UPDATE while joining tables on SQLite?
(2 answers)
Closed 6 years ago.
My select statement finds the records I want to update- Now I want to invert (multiply x -1) the adjusted_sentiment score for only these records. Here is the select statement:
Select players.name, fbposts.company, fbposts.post_id, reactions.reaction,
fbposts_comments.adjusted_sentiment, fbposts_comments.message, fbposts.message from fbposts
join reactions on reactions.post_id = fbposts.post_id
join players on players.id = reactions.id
join fbposts_comments on fbposts_comments.post_id = fbposts.post_id
where adjusted_sentiment > 0 and reactions.reaction like "ANGRY" and
reactions.id = fbposts_comments.comment_id group by fbposts.post_id
This returns records like:
Baktiyar Romanov,WorldOfWanderlust,387990201279405_994067924004960,ANGRY,0.5965,probably fed very ill-mannered rich man
Sheridan Toms,australiapost,96085205666_10153485650690667,ANGRY,0.04676666666666666,Seriously? You can't even get an express post parcel from victoria to wa in under 2 weeks!!!! Super annoyed
Robert Smissen,australiapost,96085205666_10153487649895667,ANGRY,0.8555,Looks like Australia Post is using Diggers' letters to gain some reflected glory
Eve Ismaiel,australiapost,96085205666_10153500759100667,ANGRY,0.1133333333333333,"Ha ha... Present $20, postage $30!!!"
What I want to do is invert the adjusted_sentiment score. For example in the first record, the adjusted_sentiment score is 0.5965 I want to update that to -0.5965
BTW my queries and updates will be done via Python2.7... One thought I working on now is to create a list from the query above then use that list to create a series of update statements.
Below query will give you the expected output
update fbposts_comments set adjusted_sentiment = -adjusted_sentiment where post_id in (Select fbposts_comments.post_id from fbposts
join reactions on reactions.post_id = fbposts.post_id
join players on players.id = reactions.id
join fbposts_comments on fbposts_comments.post_id = fbposts.post_id
where adjusted_sentiment > 0 and reactions.reaction like "ANGRY" and
reactions.id = fbposts_comments.comment_id group by fbposts.post_id)

How to Extract Numeric Range from Text in SQL

I am fairly new to SQL and I'm trying to extract some data from an ORACLE DB. My goal is to return rows where the query value lies in the range speicified by the "AA_SYNTAX" column in a table.
For example:
If the "AA_SYNTAX" column is 'p.G12_V14insR' and my search value is 13, I want to return that row. This column is organized as p.%number_%number%. So basically I want to return the two numerical values from this column and see if my query value is between them.
I have all the tables I need joined together and everything, just not sure how to construct a query like this. I know in regex I would do something like "\d+" but im not sure how to translate this into SQL.
Thanks
Using Oracle, you can use Regular Expressions to extract a number from the string.
More specifically, I would look into REGEXP_SUBSTR.
Using the date given in your example above, you could use:
with cte as
(
select 'p.G12_V14insR' as AA_SYNTAX from dual
)
select
REGEXP_SUBSTR(AA_SYNTAX,'p\.[[:alpha:]]+([[:digit:]]+)', 1, 1, NULL, 1) as Bottom
,REGEXP_SUBSTR(AA_SYNTAX,'\_[[:alpha:]]+([[:digit:]]+)', 1, 1, NULL, 1) as Top
from cte
I'm sure you could clean up the Regular Expression quite a bit, but, given this, you get the value of 14 for Top and 12 for Bottom.
Hope this helps move you in the right direction.

How to avoid CTE or subquery in SQL?

Question
Say we have 1 as foo, and we want foo+1 as bar in SQL.
With CTE or subquery, like:-
select foo+1 as bar from (select 1 as foo) as abc;
We would get (in postgre which is what I am using):-
bar
-----
2
However, when I tried the following:-
select 1 as foo, foo+1 as bar;
The following error occurs:-
ERROR: column "foo" does not exist
LINE 1: select 1 as foo, foo+1 as bar;
^
Is there any way around this without the use of CTE or subquery?
Why do I ask?
I am using Django for a web service, to order and paginate objects in the database, I have to grab the count of the upvotes and downvotes and do some extra mathematical manipulation on those two values (ie. calculating the wilson score interval), where those two values are used multiple times.
All I can work with that I know of right now is the extra() function without breaking the ORM(?) [for example lazy queryset and prefetch_related() function].
Therefore I need a way to call those two values from somewhere instead of doing a SELECT multiple times when I calculate the score. (Or that's not the case in reality anyway?)
PS. Currently I am storing the vote count as database field and update them, but I already have a model of a vote, so it seems redundant and slow to update vote count and insert vote to database
No, you need the sub-query or CTE to do that. There is one alternative though: create a stored procedure.
CREATE FUNCTION wilson(upvote integer, downvote integer) RETURNS float8 AS $$
DECLARE
score float8;
BEGIN
-- Calculate the score
RETURN score;
END; $$ LANGUAGE plpgsql STRICT;
In your ORM you now call the function as part of your SELECT statement:
SELECT id, upvotes, downvotes, wilson(upvotes, downvotes) FROM mytable;
Also makes for cleaner code.

Compare column value against list of regex values stored in another table and update accordingly

I am new to Oracle programming.
I want to check the "msg" value of "Table1" against the "regex" values from "Table2".
If the regular expression matches as such, I want to update the respective "regex_id" in "Table1".
Usual query: SELECT 'match found' FROM DUAL WHERE REGEXP_LIKE('s 27', '^(s27|s 27)')
Table1
MSG REG_EXID
Ss27 ?
s27 ?
s28 ?
s29 ?
Table2
REGEX REG_EXID RELEVANCE
^(s27|s 27) 1 10
^(s29|s 29) 2 2
^(m28|m 28) 3 2
^(s27|s 27) 4 100
Taking the newly added "relevance" into account, with Oracle 11g you could try along
UPDATE Table1 T1
SET T1.reg_exID =
(SELECT DISTINCT
MAX(reg_exID) KEEP (DENSE_RANK FIRST ORDER BY relevance DESC) OVER (PARTITION BY regex)
FROM Table2
WHERE REGEXP_LIKE(T1.msg, regex)
)
;
See SQL Fiddle.
You could work along
UPDATE Table1
SET reg_exID = (SELECT reg_exID FROM Table2 WHERE REGEXP_LIKE(Table1.msg, regex));
Please keep in mind:
None of your current sample records will be updated as REGEX are case sensitive.
The above UPDATE will fail, if more than a single REGEX does match.
You could rewrite the current REGEX expressions along "^m ?28".
See it in action: SQL Fiddle (With some data added to actually show the effect.)
Please comment if and as clarification/adjustment is required.