how to write for loop with nested if statement in xquery - xquery

I am trying the below code :-
return
fn:concat (fn:string-join ((
"somevalue.1.",
"somevalue.2.",
"some val 3",
"some val4",
$somevariable), " "),
for $i in $loopvar
if ((fn:exists($loopvar)) and (fn:count($loopvar) > 1)) then
" where ( " || $loopvar[i] || " and "
else if(fn:exists($loopvar) and (fn:count($loopvar) > 0)) then
" where " || $loopvar[i]
else() )
This code is forming some part of a sql query in which i am trying to get the where conditions separated by "and". The variable $loopvar is of type string and it can have multiple values like column1=3, column2=4, column3=5...and so on. I want to check if this variable has some values then it form the query(as per the above code). However I am getting error in the fn:concat part:-
fn:concat( fn:string-join( ("some val 1", "someval2"......" -- arg1 is not of type xs:anyAtomicType?
Can someone pls suggest where am I going wrong with the above piece of code?

You list MarkLogic as a tag. If so, i would consider using the Optic API where you can more elegantly express your exact query.

Your code has several issues.
In your first return statement, you are returning the fn:concat(). You could have that as the end of the first FLWOR statement, but the second FLWOR is referencing the $loopvar. IF that is in the scope of the first FLWOR, then you need to ensure that the second FLWOR is one of a sequence of items returned in the first. Wrap both the concat and that for loop inside of parenthesis to indicate that you want to return a sequence of items that are all within the scope of that first FLWOR.
The error message is telling you that the if statement following the for is unexpected, because in that FLWOR statement you started a for loop and then have an expression that you want to return, so you need to add a return statement.
https://www.w3.org/TR/xpath-full-text-10/#prod-xquery10-FLWORExpr
(ForClause | LetClause)+ WhereClause? OrderByClause? "return" ExprSingle
Lastly, it seems you are iterating over the $loopvar sequence and attempting to use $i as an index in a predicate, but forgot the $ for the $i variable i.e. $loopvar[$i]. However, unless the $i is a number, that probably won't select what you want. And if it is, it's still easier and more efficient to just use the $i variable:
(: I added some dummy variables, just to have a more complete example that would run :)
let $somevariable := "somevariable"
let $loopvar := ("a", "b", "c")
return (
fn:concat (fn:string-join ((
"somevalue.1.",
"somevalue.2.",
"some val 3",
"some val4",
$somevariable), " ")),
for $i in $loopvar
return
if ((fn:exists($loopvar)) and (fn:count($loopvar) > 1))
then " where ( " || $i || " and "
else
if(fn:exists($loopvar) and (fn:count($loopvar) > 0))
then " where " || $i
else()
)
If you did want the position of the $i in your for loop, you can use the positional variable at expression and assign a position variable: for $i at $j in $loopvar
https://www.w3.org/TR/xpath-full-text-10/#doc-xquery10-ForClause

Related

How to count equal values of the same element name [xQuery]

Here is an example:
`
<bracketQualifier>
<bracketSequenceNumber>1</bracketSequenceNumber>
</bracketQualifier>
<bracketQualifier>
<bracketSequenceNumber>1</bracketSequenceNumber>
</bracketQualifier>
<bracketQualifier>
<bracketSequenceNumber>1</bracketSequenceNumber>
</bracketQualifier>
`
What i need to do is if bracketSequenceNumber holds the same value trow an exception.
Number of elements is N there can be more than 3. How can i achieve this using xquery.
I tried something like this without success and i cant say i understand xQuery completley:
`
let $count := ( for $bracketSequenceNumber in $bracketQualifier/bracketSequenceNumber return count(bracketQualifier[#bracketSequenceNumber = $bracketSequenceNumber ])) return
if($GDSN_PriceSyncPriceSegmentTM/value ='250' and $count >= 1) then something
`
You can use
if (count(//bracketSequenceNumber)
!= count(distinct-values(//bracketSequenceNumber) then ...
If you actually want to find the duplicates, use group by in XQuery 3.1 to process each group of equal values and test whether the group size is 2 or more.

Joing lists containing Hebrew in Tcl?

I'm using Tcl 8.6.11, SQLite 3.35.5., and Manjaro Linux KDE.
I'm trying to take a verse in Hebrew and write it one word per row in a data table. This is one
verse, for example.
בְּרֵאשִׁית בָּרָא אֱלֹהִים אֵת הַשּׁמַיִם וְאֵת הָאָֽרֶץ׃
The goal was to write the data to a list and then join the list as the values part of a SQL insert statement. As you can see, each [lindex $l n] prints as expected but the [join $l ,] starting at the second element places the Hebrew in the first position instead of the last, causing the SQL statement to fail.
How can I get each component of the [join $l ,] to be ordered as they are in the [lindex $l n]?
Thank you.
set l {}
set sql { select * from src_original where type_no=0 and book_no < 40 limit 1}
dbws eval $sql origLang {
set i 0
foreach { x } $origLang(original) { lappend l "($origLang(book_no),$origLang(chapter_no),$origLang(verse_no),[incr i],$x)" }
}
puts [lindex $l 0]; # (1,1,1,1,בְּרֵאשִׁית)
puts [lindex $l 1]; # (1,1,1,2,בָּרָא)
puts [lindex $l 2]; # (1,1,1,3,אֱלֹהִים)
puts [lindex $l 3]; # (1,1,1,4,אֵת)
puts [lindex $l 4]; # (1,1,1,5,הַשּׁמַיִם)
puts [lindex $l 5]; # (1,1,1,6,וְאֵת)
puts [lindex $l 6]; # (1,1,1,7,הָאָֽרֶץ׃)
set v [join $l ,]; # (1,1,1,1,בְּרֵאשִׁית),(1,1,1,2,בָּרָא),(1,1,1,3,אֱלֹהִים),(1,1,1,4,אֵת),(1,1,1,5,הַשּׁמַיִם),(1,1,1,6,וְאֵת),(1,1,1,7,הָאָֽרֶץ׃)
set r "insert into vowel_pts (book_no, chapter_no, verse_no, index_no, word) values $v"
dbws eval $r
Thank you for examples and suggestions. I'd still like to understand whether or not join resulted in an out of order SQL statement, but, after looking at the SQL provided by #Shawn, I tried using the SQLite JSON extension and the following also works. If the limitations in the where clause of the arr_vp table are removed, such that all the words from every verse in the thirty-nine books of the Old Testament are written as an individual row, it completes in a few seconds on my ten-year old average laptop, as #DonalFellows suggested. Thanks again.
with
arr_vp as (
select book_no, chapter_no, verse_no,
'["' || replace(original,' ', '","' ) || '"]' as t
from src_original
where book_no=1
and chapter_no=1
and verse_no < 3
and type_no=0
)
select a.book_no, a.chapter_no, a.verse_no,
(key+1) as index_no,
j.value as vowel_pts
from arr_vp a,
json_each( ( select t
from arr_vp r
where r.book_no=a.book_no
and r.chapter_no=a.chapter_no
and r.verse_no=a.verse_no ) ) as j
where j.type = 'text';
As always with SQL, use parameters in a prepared statement instead of trying to add values directly into a query string at runtime. Something like:
# Populate an array of dicts
set l {}
set sql {select * from src_original where type_no=0 and book_no < 40 limit 1}
dbws eval $sql origLang {
set i 0
foreach x $origLang(original) {
lappend l [dict create book_no $origLang(book_no) \
chapter_no $origLang(chapter_no) \
verse_no $origLang(verse_no) \
index_no [incr i] \
word $x]
}
}
# And insert them one at a time.
foreach w $l {
dict with w {
dbws eval {
INSERT INTO vowel_pts(book_no, chapter_no, verse_no, index_no, word)
VALUES ($book_no, $chapter_no, $verse_no, $index_no, $word)
}
}
}
See the documentation for more about embedding (unevaluated) variable names in a SQL statement and binding values to them.
I did manage to come up with a way to do it in just core Sqlite3, assuming a standard space character separates words, that I think will work:
dbws eval {
WITH verse AS (SELECT * FROM src_original WHERE type_no = 0 AND book_no < 40 LIMIT 1),
words AS
(SELECT book_no, chapter_no, verse_no,
substr(original || ' ', 1, instr(original || ' ', ' ') - 1) AS word,
substr(original || ' ', instr(original || ' ', ' ') + 1) AS original,
1 AS index_no
FROM verse
UNION ALL
SELECT book_no, chapter_no, verse_no,
substr(original, 1, instr(original, ' ') - 1),
substr(original, instr(original, ' ') + 1),
index_no + 1
FROM words WHERE length(original) > 0)
INSERT INTO vowel_pts(book_no, chapter_no, verse_no, index_no, word)
SELECT book_no, chapter_no, verse_no, index_no, word FROM words
}
The join command does not alter the order of characters in memory. However, the rendering of mixed left-to-right and right-to-left scripts on the screen is… well, all over the place.
But since you're just doing this to move data from the database to the database, find a way to not bring the data itself into Tcl. It'll be astonishingly faster and safer too.

How to specify two if conditions using and in xquery?

I m trying the below code :-
if($var1=$var2) and if(fn:contains($somevar1, $somestring)
then
(do (this....)
else ()
However it doesnt work. Which logical operator can be used between those two ifs?
The second if keyword can be omitted:
if (
$var1 = $var2 and fn:contains($somevar1, $somestring)
) then (
...
) else (
...
)

SQR Procedure parameters/arguments

I'm trying to understand more about how arguments/parameters are used when calling and executing procedures. I understand that procedures that contain an parameter with a leading colon (:) pass back values to the calling DO command, however I think what it a little confusing is it appears that the variables names from what the calling DO command issues, and what the procedure (called by DO) returns don't necessarily have to be the same name. If someone could help shed some light on the following examples and explain what values are being either passed to/from or how their referenced by the issuing DO command that would be helpful.
It looks like the call to run the Get-Recursive-Reports-To (do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter) is issuing these 3 variables as parameters to the procedure Get-Recursive-Reports-To, however as I look through the Get-Recursive-Reports-To procedure, I do not see any references of the variable $Recursive_Line within this procedure, so is the procedure actually using it or what is the purpose of including it? Similar question with $_val_Position_NBR, where is this variable getting it's value assigned from?
And then within the Get-Recursive-Reports-To procedure I see a parameter - :$var_Next_EMPLID that I believe is being passed back to the calling DO in the Run-Recursion procedure, but I can not figure out how/where it uses the value passed back to it...
begin-procedure Run-Recursion
let #_Global_Counter = 0
let $Recursive_Line = ''
let $Data_Line_EMPL = ''
let $Data_Line_EMPL = $_BUSINESS_UNIT || '|' || $_BUSINESS_UNIT_DESCR || '|' || '2019' || '|' ||
$_EMPLID || '|' || $_NAME || '|' || $_DEPTID || '|' || $_DEPT_DECSR || '|' || $_JOBCODE || '|'
do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter)
let $Data_Line_EMPL = $Data_Line_EMPL || $Recursive_Line
do Write-Data-Line($Data_Line_EMPL)
end-procedure
begin-procedure Get-Recursive-Reports-To(:$val_Data_Line, $val_Current_Position_Nbr, #Recursion_Counter)
let #Recursion_Counter = #Recursion_Counter + 1
do Get-the-ReportsTo-for-the-Current-Position($val_Current_Position_Nbr, $Next_Position_Nbr, $Next_EMPLID)
do Check-For-Stop($Stop_Recursion, $val_Current_Position_Nbr, $Next_Position_Nbr, #Recursion_Counter)
if $Stop_Recursion = 'N'
let $val_Data_Line = $val_Data_Line || $Next_EMPLID || '|'
do Get-Recursive-Reports-To($val_Data_Line, $Next_Position_Nbr, #Recursion_Counter)
end-if
end-procedure
begin-procedure Get-the-ReportsTo-for-the-Current-Position($_val_Position_NBR, :$var_ReportsTo, :$var_Next_EMPLID)
let #local_counter = 0
begin-select
G.REPORTS_TO &G.Reports_to
let $var_ReportsTo= &G.Reports_To
from PS_POSITION_DATA G
WHERE G.POSITION_NBR = $_val_Position_NBR
and G.EFF_STATUS = 'A'
AND (G.EFFDT =
(SELECT MAX(G_ED.EFFDT) FROM PS_POSITION_DATA G_ED
WHERE G.POSITION_NBR = G_ED.POSITION_NBR
AND G_ED.EFFDT <= $_As_OF_Date))
end-select
begin-select
H.EMPLID &H.EMPLID
Z.NAME &Z.NAME
let $var_Next_EMPLID= &H.EMPLID
let #local_counter = #local_counter + 1
from PS_JOB H !, PS_EMPLOYEES Z
WHERE H.POSITION_NBR = $var_ReportsTo
and H.EMPL_STATUS not in ('D', 'R', 'T')
and (H.EFFDT =
(SELECT MAX(H_ED.EFFDT) FROM PS_JOB H_ED
WHERE H.EMPLID = H_ED.EMPLID
AND H.EMPL_RCD = H_ED.EMPL_RCD
AND H_ED.EFFDT <= $_As_Of_Date))
end-select
if #local_counter > 1
let $var_Next_EMPLID = $local_counter || ' ' || 'Employees in this Position'
end-if
if #local_counter = 0
let $var_Next_EMPLID = 'Position Vacant'
end-if
end-procedure
The FIRST call to Get-Recursive-Reports-To from the main procedure uses the $Recursive_Line variable. It doesn't have to be referenced again because it's internal to the procedure.
Once IN procedure Get-Recursive-Reports-To, the name of this variable becomes $val_Data_Line and is used to call back to the same procedure Get-Recursive-Reports-To which can change it. I guess ergo the name, Get-Recursive-Reports-To.
Recursion is a pain in the neck but it looks like it's being used to go up an organizational chain, finding the reports to employee id, then getting the name of that person.
Variable $var_Next_EMPLID is passed from Get-the-ReportsTo-for-the-Current-Position back to the caller as variable $Next_Emplid which is then used again to call Get-Recursive-Reports-To.
I can't tell when or why the Recursion stops since you didn't include the procedure "Check-For-Stop". Looks like there is some check - perhaps if the $Val_Current_Position_Nbr or $Next_Position_Nbr is blank or equal or something.

How to execute a complex sql statement and get the results in an array?

I would like to execute a fairly complex SQL statement using SQLite.swift and get the result preferably in an array to use as a data source for a tableview. The statement looks like this:
SELECT defindex, AVG(price) FROM prices WHERE quality = 5 AND price_index != 0 GROUP BY defindex ORDER BY AVG(price) DESC
I was studying the SQLite.swift documentation to ind out how to do it properly, but I couldn't find a way. I could call prepare on the database and iterate through the Statement object, but that wouldn't be optimal performance wise.
Any help would be appreciated.
Most sequences in Swift can be unpacked into an array by simply wrapping the sequence itself in an array:
let stmt = db.prepare(
"SELECT defindex, AVG(price) FROM prices " +
"WHERE quality = 5 AND price_index != 0 " +
"GROUP BY defindex " +
"ORDER BY AVG(price) DESC"
)
let rows = Array(stmt)
Building a data source from this should be relatively straightforward at this point.
If you use the type-safe API, it would look like this:
let query = prices.select(defindex, average(price))
.filter(quality == 5 && price_index != 0)
.group(defindex)
.order(average(price).desc)
let rows = Array(query)

Resources