I have a undirected graph and want to make a traceout. This works fine with:
WITH RECURSIVE path AS (
SELECT edge_id, start_node, end_node
FROM simulation.edge_data
WHERE start_node = 1 OR end_node = 1
UNION
SELECT e.edge_id, e.start_node, e.end_node
FROM simulation.edge_data e, path s
WHERE s.start_node = e.start_node OR s.start_node = e.end_node OR s.end_node = e.end_node OR s.end_node = e.start_node)
SELECT * FROM path;
Now I want to stop at certain nodes if the corresponding record of the node has the status = closed.
Not further investigate on not reachable nodes.
I tried to add something in the where clause:
(SELECT status FROM getRecord(e.start_node)) = open
This gives me all the correct nodes but not all edges.
I can provide more code if needed dont want to overdo if the answer is quiet simple!
Related
I have a graph model that contains three types of vertices (User, Group, Document) and two types of edges (member_of, permissions). The relationships can be expressed as:
User,Group --- member_of ---> Group (depth can be arbitrary)
Group --- permissions ---> Document (depth is 1)
I'm working to write a query that would answer "What are all of the users that have no permissions of any document?". This is a very non-selective query, as I'm not specifying an id for the User class.
I've come up with this solution:
SELECT id, name FROM User
LET $p = (
SELECT expand(outE('permissions')) FROM (
TRAVERSE out('member_of') FROM $parent.$current
)
)
WHERE $p.size() = 0
This solution appears to work, but is taking between 12-15 seconds to execute. Currently in my graph there are 10,000 Users, Groups and Documents each. There are ~10,000 permissions and ~50,000 member_of.
What is the most efficient way to check for path non-existence? Is there any way to improve the performance of my existing query or am I taking the wrong approach?
There are a few ways to improve your query. First, it isn't necessary to expand the Permissions edges, you can simply check the amount of edges stored on the query. We can also limit this check so that it stops at the first group with permissions edges, rather than checking them all (credit to Luigi D for giving me this idea). Thus the query becomes as follows.
SELECT * FROM User
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
It's hard for me to check any query improvements without a sizeable dataset, but there may be a minute improvement by using the more explicit out_Member_Of and out_Permissions properties, rather than the out(field) functions.
There might be another opportunity to slightly improve the query by 'removing' the User record from the traverse results, thus reducing the amount of records checked by the WHERE clause. This could be done via
SELECT * FROM User
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM (SELECT out('Member_Of') FROM $parent.$parent.$current)
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
The previous query can also be rearranged, although I suspect this one will be slower due to it checking all of the traversed results, rather than stopping at the first. It's just another option for you try.
SELECT * FROM User
LET $p = (TRAVERSE out('Member_Of') FROM (SELECT out('Member_Of') FROM $parent.$current))
WHERE $p.out('Permissions').size() = 0
Now I'm going to diverge away from that query. Perhaps it will be quicker to pre-compute if a group has access to docs, and then check each users group with the precomputed ones. This may save a lot of repetitive traversal.
I think the best way is to get all the Groups without docs. This way all groups with docs can be eliminated before traversing their other groups.
SELECT * FROM (SELECT FROM Group WHERE out('Permissions').size() = 0)
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
Perhaps creating and using an index will make the previous query even more performant, although the process currently seems a bit janky. Before you can create an index on out_Permissions, you need to create the property with create property Group.out_Permissions LINKBAG, and then you can create the index with CREATE INDEX hasDocument ON Groups (out_Permissions, #rid) notunique METADATA {ignoreNullValues: false} (creating the index this way seems strange, but it was the only way I could get it to work, hence my janky comment). You can then query the index with select expand(rid) from index:hasDocument where key = null, which will return all the Groups without permission edges, and that would replace SELECT FROM Group WHERE out('Permissions').size() = 0 in the previous query.
So here is the query that gets the groups with docs, and checks the users against it. It correctly returns users without groups too.
SELECT expand($users)
LET $groups_without_docs = (
SELECT FROM (SELECT FROM Group WHERE out('Permissions').size() = 0)
LET $p = (
SELECT FROM (
TRAVERSE out('Member_Of') FROM $parent.$current
) WHERE out('Permissions').size() > 0 LIMIT 1
)
WHERE $p.size() = 0
),
$users = (
SELECT FROM User
LET $groups = (SELECT expand(out('Member_Of')) FROM $current)
WHERE $groups containsall (#rid in $parent.$groups_without_docs)
)
Note I think $users = (SELECT FROM User WHERE out('Member_Of') containsall (#rid in $parent.$groups_without_docs)) should work, but it doesn't. I think this may be related to a bug I've previously posted, see https://github.com/orientechnologies/orientdb/issues/4692.
I am very interested to know if the various queries above improve your query, so please comment back.
As you said, this is a very non-selective query, so it's hard to optimize.
Have you tried to add a LIMIT to the inner query?
SELECT id, name FROM User
LET $p = (
SELECT expand(outE('permissions')) FROM (
TRAVERSE out('member_of') FROM $parent.$current
) LIMIT 1
)
WHERE $p.size() = 0
or even
SELECT id, name FROM User
LET $p = (
SELECT sum(outE('permissions').size()) as s FROM (
TRAVERSE out('member_of') FROM $parent.$current
)
)
WHERE $p[0].s = 0
I just realised that my Auto-Price Calculation doesn't fill the Prices for ListID 4.
I inserted the Prices from the SELECT shown below.
For bugg research I executed the SELECT without the WHERE part and it shows me the example data row.
I can't find the error though, why it is not shown in the complete select (it has no entry with ListID = 4).
Someone can see my mistake?
Edit: Just tried the subselect alone, it shows no rows for the requested article. Why is the NOT IN clause unaffected by this fact?
Most likely, it's because of how you are combining the artikelnummer and the auspraegungID.
I do not think you have accounted for that fact that '100' + '0' is idential to '10' + '00'.
Instead of trying to merge two fields into one, perhaps try the following?
SELECT
*
FROM
#allArticles AS allArticles
WHERE
Artikelnummer = 'IT-810260'
AND NOT EXISTS (SELECT *
FROM KHKPreisListenArtikel
WHERE ListeID = 4
AND Artikelnummer = allArticles.Artikelnummer
AND Auspraegung = allArticles.Auspraegung
)
If that still doesn't work, then you must have corresponding records in that other table, find them like this...
SELECT
*
FROM
#allArticles AS allArticles
INNER JOIN
KHKPreisListenArtikel AS Preis
ON Preis.ListeID = 4
AND Preis.Artikelnummer = allArticles.Artikelnummer
AND Preis.Auspraegung = allArticles.Auspraegung
WHERe
allArticles.Artikelnummer = 'IT-810260'
PLEASE ALSO NOTE
Please don't include images of code, please copy the code, so that we can copy it too.
Especially when the tables/fields are in another language...
EDIT
Here is a query that will show the cause of your original query to fail.
SELECT
*
FROM
#allArticles AS allArticles
INNER JOIN
KHKPreisListenArtikel AS Preis
ON Preis.ListeID = 4
AND Preis.Artikelnummer + CONVERT(VARCHAR(50), Preis.Auspraegung)
=
allArticles.Artikelnummer + CONVERT(VARCHAR(50), allArticles.Auspraegung)
WHERE
allArticles.Artikelnummer = 'IT-810260'
I created a content type and it's have many nodes. And write custom module for show on frontpage.
My nodes order:
$themeurl = '/drupal/'.path_to_theme().'/images';
$sql = "SELECT * FROM `node`
LEFT JOIN `field_data_field_anasayfa_catwalk` ON `field_data_field_anasayfa_catwalk`.`entity_id` = `node`.`nid`
WHERE type = 'designers_albums'
AND `node`.`status` = 1
AND `node`.`language` = '".$language->language."'
AND `field_data_field_anasayfa_catwalk`.`field_anasayfa_catwalk_value` = 1
ORDER BY `node`.`created` ASC";
But now, installed Weight module and want sort by weight. How can i change this option? Thank you.
Entities(nodes) weights are stored in weight_weights database table.
You can try that query
$sql = "SELECT * FROM `node`
LEFT JOIN `field_data_field_anasayfa_catwalk` ON `field_data_field_anasayfa_catwalk`.`entity_id` = `node`.`nid`
LEFT JOIN `weight_weights` AS w ON w.entity_id=`node`.`nid`
WHERE type = 'designers_albums'
AND `node`.`status` = 1
AND `node`.`language` = '".$language->language."'
AND `field_data_field_anasayfa_catwalk`.`field_anasayfa_catwalk_value` = 1
ORDER BY w.weight, `node`.`created` ASC";
Btw, in future you should consider learning Drupal database abstraction layer or EntityFieldQuery.
I try to build a menu using the datatype hierarchyid.
I have the root node and the current selected node. now I want to list of all elements that are related wetween root and selected node AND there siblings.
I get all related elements with following sql query
DECLARE #rootNode hierarchyid, #selectedNode hierarchyid
SELECT #rootNode = MenuNode FROM CMS_Menu WHERE MenuItemID = 3;
SELECT #selectedNode = MenuNode FROM CMS_Menu WHERE MenuItemID =15;
SELECT CMS_Menu.MenuNode
FROM CMS_Menu
WHERE #selectedNode.IsDescendantOf(MenuNode) = 1 /*all related elements*/
AND MenuNode.GetLevel() >= #rootNode.GetLevel() /*nothing below root*/
Now I have to do something like MenuNode.GetAncestor(1) = result for each row in the query above.
Does anyone have an idea how to get this in a sql query?
Thanks : )
Not entirely sure I understand the question but could you not do something like the following with the WHERE clause:
WHERE #selectedNode.IsDescendantOf(MenuNode.GetAncestor(1)) = 1
Tom
im trying to merge into a table.
this select doesnt find anything:
select * from dpr where dpr_qot_id=1111;
then i run this merge like the follwing:
MERGE INTO dpr d
USING (select dpr_ts, dpr_qot_id
from dpr
where dpr_qot_id = 1111
and dpr_ts = to_date('30.11.1999', 'DD.MM.YYYY')) s
on (s.dpr_ts = d.dpr_ts and s.dpr_qot_id = d.dpr_qot_id)
when not matched then
insert
(DPR_TS,
DPR_CLOSE,
DPR_OPEN,
DPR_HIGH,
DPR_LOW,
DPR_VOLUME,
DPR_QOT_ID)
values
(to_date('30.11.2010', 'DD.MM.YYYY'),
21.66,
21.75,
22.005,
21.66,
2556.00,
1111)
WHEN MATCHED THEN
UPDATE
set DPR_CLOSE = 21.66,
DPR_OPEN = 21.75,
DPR_HIGH = 22.005,
DPR_LOW = 21.66,
DPR_VOLUME = 2556.00;
and still this select doesn't find anything:
select * from dpr where dpr_qot_id=1111;
What am i doing wrong?
Thank you!
Greetings
Magda
Since there are no dpr rows where dpr_qot_id=1111, the source (USING) query of your MATCH will also contain no rows, so there is no data to be merged - and so nothing is done.