Postgres query issues - postgresql

I'm not sure why this structure is not working. This is my first time working with postgres and was hoping someone could help me.
SELECT * FROM "friends" WHERE "from" = '1' OR "to" = '1' AND "status" = '1'
It returns all values where from where "from" is = 1 and "to" = 1 rather than one or the other where "status" is = 1
I hope that isn't too confusing.
Thanks.

OR operator has lower precedence than AND [1]. As a result, the expression is evaluated as follows:
(
"from" = '1'
)
OR
(
"to" = '1'
AND
"status" = '1'
)
What you probably want instead is:
SELECT * FROM "friends" WHERE ("from" = '1' OR "to" = '1') AND "status" = '1'

Related

Need an alternative way to use a table name as a function parameter in PostgreSQL

Is there any other way to use a table name as a function parameter in PostgreSQL than with Execute?
The problem is the existing statement (taken from a report in openMaint) is long and has a lot of existing variables.
I want to be able to make a general function that can be used for any similar report where all I need to do is pass the different table parameters instead of making a new function for each report. However things will get exceedingly complicated using Execute. Does anyone have any alternatives for me?
CREATE TEMP TABLE "ReportData" AS
SELECT lang,
coalesce((SELECT "Description" FROM "Site" WHERE "Id" = site_id), '')::varchar site_descr,
coalesce((SELECT "Description" FROM "Division" WHERE "Id" = divis_id), '')::varchar div_descr,
coalesce(cons."Description", '')::varchar consb_descr,
coalesce(cat."Code", '')::varchar cat_code,
coalesce(_cm3_translation_lookup_get(cat."Id", lang), '')::varchar cat_descr,
coalesce(_cm3_translation_lookup_get(scat."Id", lang), '')::varchar scat_descr,
coalesce(ssite."Description", '')::varchar srcsite_descr,
coalesce(sdiv."Description", '')::varchar srcdiv_descr,
coalesce(dsite."Description", '')::varchar dstsite_descr,
coalesce(ddiv."Description", '')::varchar dstdiv_descr,
coalesce(to_char(wrmov."Date", 'DD TMMon YYYY'), '')::varchar wm_date,
coalesce(wmrow."Quantity", 0.00)::numeric wmr_quantity,
(CASE WHEN site_id IS null THEN
CASE cat."Code"
WHEN 'Load' THEN '+1'
WHEN 'Relocation' THEN '0'
WHEN 'Unload' THEN '-1'
ELSE null
END
ELSE
CASE WHEN (wrmov."SrcSite" IS null OR wrmov."SrcSite" != ALL(sites_set)) AND wrmov."DstSite" = ANY(sites_set) THEN '+1'
WHEN wrmov."SrcSite" = ANY(sites_set) AND wrmov."DstSite" = ANY(sites_set) THEN '0'
WHEN wrmov."SrcSite" = ANY(sites_set) AND (wrmov."DstSite" IS null OR wrmov."DstSite" != ALL(sites_set)) THEN '-1'
ELSE null
END
END)::varchar qt_symbol
FROM "GAWrhMovement" wrmov
LEFT JOIN "LookUp" cat ON cat."Id" = wrmov."Category"
LEFT JOIN "LookUp" scat ON scat."Id" = wrmov."Subcategory"
LEFT JOIN "Site" ssite ON ssite."Id" = wrmov."SrcSite"
LEFT JOIN "Division" sdiv ON sdiv."Id" = wrmov."SrcDivision"
LEFT JOIN "Site" dsite ON dsite."Id" = wrmov."DstSite"
LEFT JOIN "Division" ddiv ON ddiv."Id" = wrmov."DstDivision"
JOIN "GAWrhMovementRow" wmrow ON wmrow."WrhMovement" = wrmov."Id" AND wmrow."Status" = 'A'
LEFT JOIN "GAConsumable" cons ON cons."Id" = wmrow."Consumable"
WHERE CASE WHEN site_id IS null THEN true ELSE (wrmov."SrcSite" = ANY(sites_set) OR wrmov."DstSite" = ANY(sites_set)) END
AND CASE WHEN divis_id IS null THEN true ELSE (wrmov."SrcDivision" = ANY(divis_set) OR wrmov."DstDivision" = ANY(divis_set)) END
AND wrmov."Status" = 'A'
ORDER BY consb_descr, wrmov."Date";
No, there is no other way than to use dynamic SQL with EXECUTE.

clean way to detect if current_query() is a prepared statement?

Does anyone know a good way to detect if the result from current_query()is a prepared statement or not?
I seems that I can't simply use a string function because this would be an exampe for a prepared statement:
UPDATE table SET "x" = $1 WHERE "y" = $2 AND "z" = $3
But this would not:
UPDATE table SET "x" = '$1 + $2 = $3' WHERE "y"='$1' AND "z" = 1
Is there maybe another function I can use together with / instead of current_query() or do you have any other ideas?
You may be able to detect if current_query() is a prepared statement by looking for \$[[:digit:]] after stripping the text of all strings. The following query would do, however it may fail in cases of intricate quote nesting:
with
queries(curr_query) as (
values ($$UPDATE table SET "x" = '$1||''a'' + $2 = $3' WHERE "y"='$1' AND "z" = 1$$),
($$UPDATE table SET "x" = $r1$a$r1$||$1 WHERE "y" = $2 AND "z" = $3||$r1$b$r1$ $$),
($$UPDATE table SET "x" = $1 WHERE "y" = $2 AND "z" = $3$$)
),
stripped as (
select *,
regexp_replace(
regexp_replace(
regexp_replace(curr_query, '(["'']).*?\1', '', 'g'),
'\$([[:alpha:]]*?)\$.*?\$\1\$', '', 'g'),
'\$([[:alpha:]][[:alnum:]]*?)\$.*?\$\1\$', '', 'g') as stripped_query
from queries
)
select *, stripped_query ~ '\$[[:digit:]]' AS is_prepared
from stripped

OrientDB SQL - traverse while keeping edges weight

Lets assume the following model in OrientDB graph:
I have a Profile vertex.
Profiles are connected with 2 edges: Liked and Commented. Both edges have a "value" field indicating the count of the action (or the "weight" of the edge).
So, if user A commented 3 times on posts by user B there will be a Commented edge from user A to user B with value = 3.
Now, say I want to get all the users that interacted with user B (either liked or commented), sorted by the weight of the interaction.
I can do that with the following SQL:
select * from (traverse out from
(select out, sum(value) as value from
(traverse * from (select from Profile where username="B") while $depth < 3)
where #class="Liked" or #class="Commented" group by out order by value desc)
while $depth < 2 ) where #class="Profile" )
But what if I want to know also the weight of the interaction? How do I propagate up the "value" while doing the last traverse?
Edit
According to the suggestion, a simplified version of this query will be:
select expand(out) from (
select out, sum(value) as value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) group by out order by value desc
)
But I still can't find a way to use LET to insert the value into the outer expanded object. $parent does not seem to point to the object that is expanded on the most outer select.
Edit 2
I'm Playing with $parent in every way I can think of. I don't see how you can use it in this case. Again - the problem I'm trying to solve is how to pass the sum(value) to the outer result set. I don't see a way of using LET for it when doing a GROUP BY, and I also don't see a way of using LET when the outer most select is doing an expand (since you can't do other projections together with expand).
Also, the results of using $current do not seem to be what is expected. For example, the following query:
select expand($v) from
(select from
(select expand(inE("Liked", "Commented")) from Profile where #rid=#11:0)
let $v = $current
)
Returns this:
{
"result" : [{
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}
]
}
The same node over and over again, instead of all the edges, which is what I would expect.
I see you're using an old version of OrientDB. With more recent versions you can simplify it by. Example: original query:
select * from (
traverse out from (
select out, sum(value) as value from (
traverse * from (
select from Profile where username="B"
) while $depth < 3
) where #class="Liked" or #class="Commented" group by out order by value desc
) while $depth < 2
) where #class="Profile" )
You could skip some step by using out()/in()/both() passing the Edge's labels/class like:
select expand( out(["Liked","Commented]) ) from Profile where username="B"
However to pass the value you can use variables with LET clause. Example:
select from XXX let $parent.a = value
In this way you set the variable "a" into the upper level context, but you could do also:
select from XXX let $parent.$parent.a = value
To set it 2 levels up.
I haven't tried this with a group by yet, but you should be able to group the result using a sub query. This works for me, where prop1, prop2, prop3, etc are properties of the vertices coming out of the edge (the columns resulting from select out() Profile where username="B")
select outV().prop1, outV().prop2, outV().prop3, value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) order by value desc

Deleted-table used in instead of update trigger

I got the following code ( no chance to ask the person who wrote it ) and don't see the need of the inner join on deleted. Do I miss something?
I thought, the deleted-table is used in instead-of-delete triggers. In instead-of-inserted triggers it's empty everytime, isn't it in instead-of-update triggers?
CREATE TRIGGER "someTrigger" ON "dbo"."someView"
INSTEAD OF UPDATE
AS
BEGIN
INSERT INTO "someOtherTable"
(
"id"
, "startTime"
, "endTime"
, "duration"
, "resourceId"
, "resourceLocked"
, "timeLocked"
, "groupPrefix"
)
SELECT
"id" = i."id"
, "startTime" = i."startTime"
, "endTime" = i."endTime"
, "duration" = ISNULL( i."duration", 0 )
, "resourceId" = i."resourceId"
, "resourceLocked" = ISNULL( i."resourceLocked", 0 )
, "timeLocked" = ISNULL( i."timeLocked", 0 )
, "groupPrefix" = N'gp'
FROM inserted AS i
INNER JOIN deleted AS d
ON d."id" = i."id"
WHERE ( i."jobType" != 3 )
OR ( i."jobType" = 3 AND i."startTime" != d."startTime" );
END;
In an update trigger, both inserted and deleted are populated. inserted contains the new row values, deleted contains the old row values, i.e. the values from before the UPDATE statement executed.
Here, it seems to be being used in the WHERE clause:
WHERE ( i."jobType" != 3 )
OR ( i."jobType" = 3 AND i."startTime" != d."startTime" );
So, if jobType is 3, we only do the insert if the startTime has been changed.
It should be noted that correlating the rows between inserted and deleted is only possible if at least one key's column(s) are not subject to update. Here, id appears to be a key. Hopefully that id column isn't subject to update or else this trigger may misbehave when such updates occur.

Selecting a set of three distinct objects

Drools Planner used this to select two distinct objects, ensuring that a rule did not fire twice for a given pair of objects. If o1 and o2 made a match, it fired only once, not twice for (o1,o2) and (o2,01).
when
$stp1 : SubjectTeacherPeriod( $slno : slNo,
$id : id,
$period_day_order : period.dayOrder
)
$stp2 : SubjectTeacherPeriod( slNo > $slno,
id == $id,
period.dayOrder == $period_day_order
)
How do I select a set of three distinct objects? What is a good selection criteria for that?
Same approach should work:
$f1 : SomeFact( ... )
$f2 : SomeFact( id > $f1.id, ... )
$f3 : SomeFact( id > $f2.id, ... )