Querying by Keys with LogicBlox - logicblox

If I have two predicates (not functional):
addblock 'city(city_dim_id) -> int(city_dim_id).'
addblock 'city_name[city_dim_id] = name -> int(city_dim_id), string(name).'
I can add facts:
exec '+city(1).'
exec '+city_name[0] = "N/A".'
exec '+city_name[1] = "Chicago".'
These are then queries of facts in the predicates:
query '_(city_name) <- city_name(city_name, _).'
query '_(city_name) <- city_name(_, city_name).'
query '_(city_dim_id, city_name) <- city_name(city_dim_id, city_name).'
My question is how do I make a query to show
1. what are the city_dim_id in both tables,
2. return city_dim_id and city_name, but only where city_dim_id present in both tables?
Thanks in advance.

Sorry I'm struggling to understand the question.
The following will return the city_dim_id's that have the same city_name.
_(c1, c2) <-
city(c1),
city(c2),
city_name[c1] = city_name[c2],
c1 != c2.

If by ' city_dim_id in both tables ' you mean 'city_dim_id which are in both tables' then you want
_(id) <-city(id), city_name[id] = _.
if on the other hand you want the id who are in either table, you need to replace the conjunction by a disjunction.
_(id) <- city(id); city_name[id] = _.
I think you want
_(id,name) <- city(id), city_name[id] = name.
note: if you use the square bracket syntax city_name[id] = name then the predicate WILL be functional

Related

How to perform WHERE in with multiple columns in postgres

I wants to do something like this,
SELECT * FROM product p
JOIN product_version pv ON p.id = pv.product_id
where (p.code, pv.product_version) in (("FF6",1), ("FF12", 1));
But this is giving error at in clause.
Can someone provide the correct syntax.
You are not providing any information about the actual error neither about column types.
But, by the way, it really looks like that those double quotes are wrong because in Postgres strings are quoted using simple quotes ('), not double (").
Try:
SELECT *
FROM product p
JOIN product_version pv ON (p.id = pv.product_id)
where
(p.code, pv.product_version) in (('FF6',1), ('FF12', 1))
;
Despite that, your query looks syntactically "correct" unless some kind of type mismatching we cannot foresee without more information.
You probably can't go with IN, depending on your goal you need to do something like:
where (p.code = "FF6" and pv.product_version = 1) or
(p.code = "FF12" and pv.product_version = 1)
or, if the logic above was not what you meant, maybe:
where p.code IN ("FF6", "FF12} AND pv.product_version IN (1)
or
where p.code IN ("FF6", "FF12} OR pv.product_version IN (1)
This code should work for you
SELECT * FROM product p
JOIN product_version pv ON p.id = pv.product_id
where p.code in("FF6","FF12") and pv.product_version = 1

create an empty table which gets the column names from another table

We have two tables 't' and 's'.
These tables may or may not have data but the schema of both t and s will alwaya be same.
Tables:
q)t:([] id:("ab";"cd";"ef";"gh";"ij"); refid:("";"ab";"";"ef";""); typ:`BUY`SELL`BUY`SELL`BUY)
q)s:t / For example purpose
Now, in my function. I want to concatenate the output of these two tables and return it, for which I'm using variable named res.
The problem is initially res is empty and not of type 98h, hence if I try to join t or s to res then it fails(which is obvious).
q){$[not ((count res) ~ 0); res: res,t ; res:t ]; $[not ((count res) ~ 0); res: res,s ; res:s ]; :res}[]
'res
One solution to this is create an empty schema for res(same as t and s table) and it works perfectly.
q){res:([] id:(); refid:(); typ:`$());$[not ((count res) ~ 0); res: res,t ; res:t ]; $[not ((count res) ~ 0); res: res,s ; res:s ]; :res}[]
But, is there a way that we don't have to create empty schema for res with all columns before hand, rather assign res as null(empty) table which can get the schema same as t or s when t or s is joined with res.
Your example isn't entirely clear - you mention res already exists in a comment, but then state that "initially res is empty and not of type 98h".
If you only want to assign res to be an empty table if it doesn't already exist, you can use a system command to check if res has already been defined in the root namespace, like the below:
f:{
if[not res in system"a";res:()]
$[count res;res,:t;res:t];
$[count res;res,:s;res:s];
:res;
};
Assign res to be 0 take the schema in question.
q)t:([] id:("ab";"cd";"ef";"gh";"ij"); refid:("";"ab";"";"ef";""); typ:`BUY`SELL`BUY`SELL`BUY)
q)res:0#t
q)meta res
c | t f a
-----| -----
id |
refid|
typ | s
So in this case you can do the following
q){[]res:0#t;if[count res;res,:t];$[count res;res,:s;res:s]}[]

SELECT with substring in ON clause?

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

F# Navigate object graph to return specific Nodes

I'm trying to build a list of DataTables based on DataRelations in a DataSet, where the tables returned are only included by their relationships with each other, knowing each end of the chain in advance. Such that my DataSet has 7 Tables. The relationships look like this:
Table1 -> Table2 -> Table3 -> Table4 -> Table5
-> Table6 -> Table7
So given Table1 and Table7, I want to return Tables 1, 2, 3, 6, 7
My code so far traverses all the relations and returns all Tables so in the example it returns Table4 and Table5 as well. I've passed in the first, last as arguments, and know that I'm not yet using the last one yet, I am still trying to think how to go about it, and is where I need the help.
type DataItem =
| T of DataTable
| R of DataRelation list
let GetRelatedTables (first, last) =
let rec flat_rec dt acc =
match dt with
| T(dt) ->
let rels = [ for r in dt.ParentRelations do yield r ]
dt :: flat_rec(R(rels)) acc
| R(h::t) ->
flat_rec(R(t)) acc # flat_rec(T(h.ParentTable)) acc
| R([]) -> []
flat_rec first []
I think something like this would do it (although I haven't tested it). It returns DataTable list option because, in theory, a path between two tables might not exist.
let findPathBetweenTables (table1 : DataTable) (table2 : DataTable) =
let visited = System.Collections.Generic.HashSet() //check for circular references
let rec search path =
let table = List.head path
if not (visited.Add(table)) then None
elif table = table2 then Some(List.rev path)
else
table.ChildRelations
|> Seq.cast<DataRelation>
|> Seq.tryPick (fun rel -> search (rel.ChildTable::path))
search [table1]

Please help transform Tsql "implicit joins" into explicit ones

Sorry, I am pretty much an SQL noob. This has to work in MSFT SQL, Oracle as well as Sybase. In the following snippet I need to change an inner join between IJ and KL on IJ.PO_id = KL.PO_id into a left join also on IJ.PO_id = KL.PO_id. So, I believe I have to re-factor this. Well, implicit joins are not the most readable, at least in my co-worker's eyes. I guess I will agree until I develop my own taste. Sorry, I mangled the table and field names just in case.
/* #IJ_id is an input stored proc patrameter. */
from AB,
CD,
EF,
GH,
IJ,
KL
where
EF.EF_id = IJ.EF_id and
IJ.EF_id = AB.EF_id and
EF.ZY_id = IJ.ZY_id and
IJ.ZY_id = AB.ZY_id and
IJ.IJ_id = AB.IJ_id and
IJ.IJ_id = #IJ_id and
EF.XW_id = GH.GH_id and
AB.VU_code = CD.VU_code and
IJ.TS > 0 and
IJ.RQ = 0 and
EF.RQ = 0 and
AB.RQ = 0 and
IJ.PO_id = KL.PO_id;
Now, my difficulty is that there is a lot going on in the where clause. Things that do not look like a.b = c.d will remain in the where clause, but not all stuff that does look like a.b = c.d look easy to convert into an explicit join. The difficult part is that ideally the conditions would be between neighbors - AB+CD, CD+EF, EF+GH, GH+IJ, IJ+KL but they are not that organized right now. I could re-order some, but ultimately I do not want to forget my goal: I want the new query to be no slower, and I want the new query to be no less readable. It seems that I might be better off hacking just the part that I need to change, and leave it mostly the same. I am not sure if I can do that.
If you understood my intent, please suggest a better query. if you did not, then please tell me how I can improve the question. Thanks.
I think it should be something like this:
FROM AB
JOIN CD ON AB.VU_code = CD.VU_code
JOIN IJ ON IJ.EF_id = AB.EF_id AND IJ.ZY_id = AB.ZY_id AND IJ.IJ_id = AB.IJ_id
JOIN EF ON EF.EF_id = IJ.EF_id AND EF.ZY_id = IJ.ZY_id
JOIN GH ON EF.XW_id = GH.GH_id
JOIN KL ON IJ.PO_id = KL.PO_id
WHERE
IJ.IJ_id = #IJ_id AND
IJ.TS > 0 AND
IJ.RQ = 0 AND
EF.RQ = 0 AND
AB.RQ = 0
I have tried to arrange the tables such that the following rules hold:
Every join condition mentions the new table that it joining on one side.
No table is mentioned in a join condition if that table has not been joined yet.
Conditions where one of the operands is a constant are left as a WHERE condition.
The last rule is a difficult one - it is not possible to tell from your mangled names whether a condition ought to be part of a join or part of the where clause. Both will give the same result for an INNER JOIN. Whether the condition should be part of the join or part of the where clause depends on the semantics of the relationship between the tables.
You need to consider each condition on a case-by-case basis:
Does it define the relationship between the two tables? Put it in the JOIN.
Is it a filter on the results? Put it in the WHERE clause.
Some guidelines:
A condition that includes a parameter from the user is unlikely to be something that should be moved to a join.
Inequalities are not usually found in join conditions.
It couldn't possibly get any less readable than the example you gave...
from AB a
join CD c on a.VU_Code = c.VU_Code
join EF e on a.EF_id = e.EF_id and e.RQ = 0
join GH g on e.XW_id = g.GH_id
join IJ i on a.IJ_id = i.IJ_id and e.EF_id = i.EF_id
and a.EF_id = i.EF_id and e.ZY_id = i.ZY_id
and a.ZY_id = i.ZY_id and i.TS > 0 and i.RQ = 0
LEFT join KL k on i.PO_id = k.PO_id
where
i.IJ_id = #IJ_id and
a.RQ = 0
Use:
FROM AB t1
JOIN CD t2 ON t2.VU_code = t1.VU_code
JOIN GH t4 ON t4.gh_id = t3.xw_id
JOIN IJ t5 ON t5.ZY_id = t1.ZY_id
AND t5.IJ_id = t1.IJ_id
AND t5.EF_id = t1.EF_id
AND t5.IJ_id = #IJ_id
AND t5.TS > 0
AND t5.RQ = 0
JOIN EF t3 ON t3.ef_id = t5.ef_id
AND t3.zy_id = t5.zy_id
AND t3.RQ = 0
JOIN KL t6 ON t6.po_id = t5.po_id -- Add LEFT before JOIN for LEFT JOIN
WHERE ab.qu = 0
They're aliased in the sequence of the original ANSI-89 syntax, but the order is adjusted due to alias reference - can't reference a table alias before it's been defined.
This is ANSI-92 JOIN syntax - there's no performance benefit, but it does mean that OUTER join syntax is consistent. Just have to add LEFT before the "JOIN KL ..." to turn that into a LEFT JOIN.