In c# I am generating a graph and am using RDF/XML to send it to my android application. Part of my graph is supposed to show different trains that arrive at a certain train station.
Adding a train, its arrival, departure, destination and so on is no problem. Unfortunately I don't know how to get all stations the different trains pass on their way in the RDF/XML.
For example:
Train A starts in city 1, passes city 2 & 3, and arrives in city 4.
Train B starts in city 1, passes city 5 & 6 & 7, and arrives in city 8.
How can I dynamically add arrays like [city 5, city 6, city 7] to a train?
My current RDF/XML:
<?xml version="1.0" encoding="utf-16"?>" +
"<!DOCTYPE rdf:RDF [\n" +
"\t<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n" +
"]>\n" +
"<rdf:RDF xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema#\" xmlns:ns0=\"http://my.url.com/ontologies/mash-up#\" xmlns:ns1=\"http://xmlns.com/foaf/0.1/\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" +
" <ns0:Train rdf:about=\"http://my.url.com/ontologies/mash-up#Train-1d4b674c-479c-48a3-aab3-b729fc96cbd4\">\n" +
" <ns0:name rdf:datatype=\"&xsd;string\">RE 1</ns0:name>\n" +
" <ns0:description rdf:datatype=\"&xsd;string\">Platform 1</ns0:description>\n" +
" <ns0:arrival rdf:datatype=\"&xsd;dateTime\">2015-04-14T18:00:40Z</ns0:arrival>\n" +
" <ns0:departure rdf:datatype=\"&xsd;dateTime\">2015-04-14T18:02:40Z</ns0:departure>\n" +
" <ns0:destination rdf:datatype=\"&xsd;string\">Padaborn</ns0:destination>\n" +
" <ns1:primaryTopic rdf:resource=\"http://my.url.com/ontologies/mash-up#Train-1d4b674c-479c-48a3-aab3-b729fc96cbd4\" />\n" +
" </ns0:Train>\n" +
"</rdf:RDF>";
Simply adding all stations as an own attribute would mean my Jena query would have to have very very many optionals and I'd like to avoid that.
Thanks in advance.
Edit:
This is how I would do it. That would mean very many optionals...
" <ns0:stations rdf:about=\"http://my.url.com/ontologies/mash-up#Stations-xxxx\">“+
" <ns0:from rdf:datatype=\"&xsd;string\">Aachen</ns0:from>\n“ +
" <ns0:to rdf:datatype=\"&xsd;string\">Padaborn</ns0:to>\n“ +
" <ns0:over1 rdf:datatype=\"&xsd;string\“>Köln</ns0:to>\n“ +
" <ns0:over2 rdf:datatype=\"&xsd;string\“>Düsseldorf</ns0:to>\n“ +
" <ns0:over3 rdf:datatype=\"&xsd;string\“>Duisburg</ns0:to>\n“ +
" <ns0:over4 rdf:datatype=\"&xsd;string\“>Essen</ns0:to>\n“ +
" <ns0:over5 rdf:datatype=\"&xsd;string\“>Dortmund</ns0:to>\n“ +
" <ns0:over6 rdf:datatype=\"&xsd;string\“>Hamm (Westf)</ns0:to>\n“ +
" </ns0:stations>\n"
If order of the stations does not matter:
The simplest option, if the order of the stations passed is not important, would be to use the same property. That would look like this in the data (I'll use Turtle, since it's more human readable and writeable, but show the corresponding RDF/XML too; it's easy to convert between the two, since they're just different serializations of the same data):
#prefix : <urn:train:>.
#prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
:train1
:name "RE 1" ;
:description "Platform 1" ;
:arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
:departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
:over "Station1", "Station2", "Station3" ;
:destination "Padaborn" .
<rdf:RDF
xmlns="urn:train:"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<rdf:Description rdf:about="urn:train:train1">
<name>RE 1</name>
<description>Platform 1</description>
<arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:00:40Z</arrival>
<departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:02:40Z</departure>
<over>Station1</over>
<over>Station2</over>
<over>Station3</over>
<destination>Padaborn</destination>
</rdf:Description>
</rdf:RDF>
Then you can use just one optional in the SPARQL query:
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
select * where {
?train :name ?name ;
:description ?description ;
:arrival ?arrival ;
:departure ?departure ;
:destination ?destination .
optional {
?train :over ?over
}
}
--------------------------------------------------------------------------------------------------------------------------------------------
| train | name | description | arrival | departure | destination | over |
============================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station3" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station1" |
--------------------------------------------------------------------------------------------------------------------------------------------
You might want those in a list, so you'd group by and group_concat their values:
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
select ?train ?name ?description
?arrival ?departure
?destination
(group_concat(?over) as ?overs)
where {
?train :name ?name ;
:description ?description ;
:arrival ?arrival ;
:departure ?departure ;
:destination ?destination .
optional {
?train :over ?over
}
}
group by ?train ?name ?description ?arrival ?departure ?destination
--------------------------------------------------------------------------------------------------------------------------------------------------------------
| train | name | description | arrival | departure | destination | overs |
==============================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station3 Station2 Station1" |
--------------------------------------------------------------------------------------------------------------------------------------------------------------
If the order matters
If the order is more important, then you'll need to preserve it somehow. You could either do this with distinct properties, or one property that has a list value (or some other structured data that supports ordering).
With multiple properties
Your solution that used over1, over2, over3, properties can actually be realized using just one optional pattern, since you can use a variable in place of a property, and then filter on the value of that. Just check whether its URI begins with the right prefix, including over:
#prefix : <urn:train:>.
#prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
:train1
:name "RE 1" ;
:description "Platform 1" ;
:arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
:departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
:over1 "Station1" ;
:over2 "Station2" ;
:over3 "Station3" ;
:destination "Padaborn" .
<rdf:RDF
xmlns="urn:train:"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<rdf:Description rdf:about="urn:train:train1">
<name>RE 1</name>
<description>Platform 1</description>
<arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:00:40Z</arrival>
<departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:02:40Z</departure>
<over1>Station1</over1>
<over2>Station2</over2>
<over3>Station3</over3>
<destination>Padaborn</destination>
</rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
select * where {
?train :name ?name ;
:description ?description ;
:arrival ?arrival ;
:departure ?departure ;
:destination ?destination .
optional {
?train ?overProp ?over .
filter strstarts(str(?overProp),str(:over))
}
}
order by ?overProp
-------------------------------------------------------------------------------------------------------------------------------------------------------
| train | name | description | arrival | departure | destination | overProp | over |
=======================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | :over1 | "Station1" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | :over2 | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | :over3 | "Station3" |
-------------------------------------------------------------------------------------------------------------------------------------------------------
And of course, you can use group by to combine the values here in the same way.
With just a little bit of structure
You could also use just one property, but have the value be something with just a little bit of structure. E.g., a blank node that has a value for the station and the index in which it's passed:
#prefix : <urn:train:>.
#prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
:train1
:name "RE 1" ;
:description "Platform 1" ;
:arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
:departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
:over [:station "Station1" ; :number 2 ] ,
[:station "Station2" ; :number 3 ] ,
[:station "Station3" ; :number 1 ] ;
:destination "Padaborn" .
<rdf:RDF
xmlns="urn:train:"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<rdf:Description rdf:about="urn:train:train1">
<name>RE 1</name>
<description>Platform 1</description>
<arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:00:40Z</arrival>
<departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:02:40Z</departure>
<over rdf:parseType="Resource">
<station>Station1</station>
<number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
>2</number>
</over>
<over rdf:parseType="Resource">
<station>Station2</station>
<number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
>3</number>
</over>
<over rdf:parseType="Resource">
<station>Station3</station>
<number rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
>1</number>
</over>
<destination>Padaborn</destination>
</rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
select * where {
?train :name ?name ;
:description ?description ;
:arrival ?arrival ;
:departure ?departure ;
:destination ?destination .
optional {
?train :over [ :station ?over ;
:number ?number ]
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------
| train | name | description | arrival | departure | destination | over | number |
=====================================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station3" | 1 |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station2" | 3 |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station1" | 2 |
-----------------------------------------------------------------------------------------------------------------------------------------------------
With a single property and a list
You could also have a single over property and have its value be a list. This is very easy to write in the N3/Turtle serialization. It's not as pretty in RDF/XML, but like I said in a comment, it's better to use a human writeable syntax if you're writing by hand, or to use a proper API for writing it. It's not too hard to query in the SPARQL either.
#prefix : <urn:train:>.
#prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
:train1
:name "RE 1" ;
:description "Platform 1" ;
:arrival "2015-04-14T18:00:40Z"^^xsd:dateTime ;
:departure "2015-04-14T18:02:40Z"^^xsd:dateTime ;
:over ("Station1" "Station2" "Station3") ;
:destination "Padaborn" .
<rdf:RDF
xmlns="urn:train:"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
<rdf:Description rdf:about="urn:train:train1">
<name>RE 1</name>
<description>Platform 1</description>
<arrival rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:00:40Z</arrival>
<departure rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
>2015-04-14T18:02:40Z</departure>
<over rdf:parseType="Resource">
<rdf:first>Station1</rdf:first>
<rdf:rest rdf:parseType="Resource">
<rdf:first>Station2</rdf:first>
<rdf:rest rdf:parseType="Resource">
<rdf:first>Station3</rdf:first>
<rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
</rdf:rest>
</rdf:rest>
</over>
<destination>Padaborn</destination>
</rdf:Description>
</rdf:RDF>
prefix : <urn:train:>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
select * where {
?train :name ?name ;
:description ?description ;
:arrival ?arrival ;
:departure ?departure ;
:destination ?destination .
optional {
?train :over/(rdf:rest*/rdf:first) ?over
}
}
--------------------------------------------------------------------------------------------------------------------------------------------
| train | name | description | arrival | departure | destination | over |
============================================================================================================================================
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station1" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station2" |
| :train1 | "RE 1" | "Platform 1" | "2015-04-14T18:00:40Z"^^xsd:dateTime | "2015-04-14T18:02:40Z"^^xsd:dateTime | "Padaborn" | "Station3" |
--------------------------------------------------------------------------------------------------------------------------------------------
And of course, you can can group by and group_concat here, too. With the list based approach, you can actually compute the position of the element in the list, too. See, e.g., my answer to Is it possible to get the position of an element in an RDF Collection in SPARQL?.
Related
I have an orders table as shown below.
+----------+-------------+---------+--------------+
| Name | orderDate | memberId| OrderId |
+----------+-------------+---------+--------------+
| Tom | 01-01-2023 | ABC | 111 |
| Dick | 01-01-2023 | XYZ | 222 |
| Harry | 01-01-2023 | PQR | 666 |
| Dick | 01-01-2023 | XYZ | 222 |
| Tom | 02-01-2023 | ABC | 111 |
| Harry | 03-01-2023 | PQR | 666 |
| Dick | 03-01-2023 | XYZ | 222 |
| Tom | 04-01-2023 | ABC | 111 |
| Dick | 06-01-2023 | XYZ | 222 |
| Dick | 07-01-2023 | XYZ | 222 |
| Harry | 04-01-2023 | PQR | 666 |
| Dick | 08-01-2023 | XYZ | 222 |
| Tom | 05-01-2023 | ABC | 111 |
| Harry | 05-01-2023 | PQR | 666 |
| Harry | 06-01-2023 | PQR | 666 |
| Harry | 07-01-2023 | PQR | 666 |
+----------+-------------+---------+--------------+
Expected Result:
I have a list of memberIds -> ABC, XYZ, PQR (This list can be any size approx 200 max)
I need to select the latest 3 orders for each member.
+----------+-------------+---------+--------------+
| Name | orderDate | memberId| OrderId |
+----------+-------------+---------+--------------+
| Tom | 01-01-2023 | ABC | 111 |
| Tom | 02-01-2023 | ABC | 111 |
| Tom | 04-01-2023 | ABC | 111 |
| Dick | 02-01-2023 | XYZ | 111 |
| Dick | 01-01-2023 | XYZ | 111 |
| Dick | 03-01-2023 | XYZ | 111 |
| Harry | 01-01-2023 | PQR | 111 |
| Harry | 03-01-2023 | PQR | 111 |
| Harry | 04-01-2023 | PQR | 111 |
+----------+-------------+---------+--------------+
How can I write an optimized select query for the above scenario?
Currently, I created a DB2 hibernate SQL query dynamically in for loop.
The problem with this approach is that my query length grows too big for DB2 and returns an error as I have 200 memberId
-101 The statement is too long or too complex
(SELECT * FROM orders WHERE memberId='ABC' ORDERBY orderDate DESC FETCH FIRST 3 ROWS ONLY)
UNION ALL
(SELECT * FROM orders WHERE memberId='XYZ' ORDERBY orderDate DESC FETCH FIRST 3 ROWS ONLY)
UNION ALL
(SELECT * FROM orders WHERE memberId='PQR' ORDERBY orderDate DESC FETCH FIRST 3 ROWS ONLY)
Probable Solutions:
Divide memberIds list into smaller batches, call select multiple times, and aggregate results in my java code.
Any other way to fetch this result from SQL?
Please share your opinion from a query or java point of view.
Thank you
You may use GLOBAL TEMPORARY TABLE to store your long member list.
DECLARE GLOBAL TEMPORARY TABLE SESSION.MEMBERS (MEMBERID CHAR(3))
WITH REPLACE ON COMMIT PRESERVE ROWS NOT LOGGED;
INSERT INTO SESSION.MEMBERS VALUES 'ABC';
...
INSERT INTO SESSION.MEMBERS VALUES 'XYZ';
WITH MYTAB (Name, orderDate, memberId, OrderId) AS
(
VALUES
(' Tom', date ('01.01.2023'), 'ABC', 111)
, (' Dick', date ('01.01.2023'), 'XYZ', 222)
, ('Harry', date ('01.01.2023'), 'PQR', 666)
, (' Dick', date ('01.01.2023'), 'XYZ', 222)
, (' Tom', date ('02.01.2023'), 'ABC', 111)
, ('Harry', date ('03.01.2023'), 'PQR', 666)
, (' Dick', date ('03.01.2023'), 'XYZ', 222)
, (' Tom', date ('04.01.2023'), 'ABC', 111)
, (' Dick', date ('06.01.2023'), 'XYZ', 222)
, (' Dick', date ('07.01.2023'), 'XYZ', 222)
, ('Harry', date ('04.01.2023'), 'PQR', 666)
, (' Dick', date ('08.01.2023'), 'XYZ', 222)
, (' Tom', date ('05.01.2023'), 'ABC', 111)
, ('Harry', date ('05.01.2023'), 'PQR', 666)
, ('Harry', date ('06.01.2023'), 'PQR', 666)
, ('Harry', date ('07.01.2023'), 'PQR', 666)
)
SELECT Name, orderDate, memberId, OrderID
FROM
(
SELECT
T.*
, ROW_NUMBER () OVER (PARTITION BY Name ORDER BY orderDate DESC) AS RN_
FROM MYTAB T
WHERE EXISTS (SELECT 1 FROM SESSION.MEMBERS M WHERE M.MEMBERID = T.MEMBERID)
) T
WHERE RN_ <= 3
ORDER BY Name, orderDate DESC;
NAME
ORDERDATE
MEMBERID
ORDERID
Tom
2023-01-05
ABC
111
Tom
2023-01-04
ABC
111
Tom
2023-01-02
ABC
111
Dick
2023-01-08
XYZ
222
Dick
2023-01-07
XYZ
222
Dick
2023-01-06
XYZ
222
fiddle
row_number() with partition by clause solves your use case.
Here is the query for the same
select rs.name, rs.order_date, rs.member_id, rs.order_id from (select *, row_number() over (partition by member_id order by order_date desc) as `order` from orders where member_id in ('XYZ', 'ABC')) as rs where rs.order <= 3;
I am using select
SELECT
asl.id, asl.outstanding_principal as outstandingPrincipal, the_date as theDate, asl.interest_rate as interestRate, asl.interest_payment as interestPayment, asl.principal_payment as principalPayment,
asl.total_payment as totalPayment, asl.actual_delta as actualDelta, asl.outstanding_usd as outstandingUsd, asl.disbursement, asl.floating_index_rate as floatingIndexRate,
asl.upfront_fee as upfrontFee, asl.commitment_fee as commitmentFee, asl.other_fee as otherFee, asl.withholding_tax as withholdingTax, asl.default_fee as defaultFee,
asl.prepayment_fee as prepaymentFee, asl.total_out_flows as totalOutFlows, asl.net_flows as netFlows, asl.modified, asl.new_row as newRow, asl.interest_payment_modified as
interestPaymentModified, asl.date, asl.amortization_schedule_initial_id as amortizationScheduleInitialId, asl.tranche_id as trancheId, asl.user_id as userId, tr.local_currency_id as localCurrencyId,
f.facility_id
FROM
GENERATE_SERIES
(
(SELECT MIN(ams.date) FROM amortization_schedules ams),
(SELECT MAX(ams.date) + INTERVAL '1' MONTH FROM amortization_schedules ams),
'1 MONTH'
) AS tab (the_date)
FULL JOIN amortization_schedules asl on to_char(the_date, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')
LEFT JOIN tranches tr ON asl.tranche_id = tr.id
LEFT JOIN facilities f on tr.facility_id = f.id
In this select, I'm using generate_series to get each month since there are no records in the database for each month. But the matter is that this select gives me superfluous results. I use this select in my Spring Boot application. But the fact is that I need all the data, and for example only with a certain facility_id , and when I insert a condition
WHERE f.id = :id and tr.tranche_number_id = :trancheNumberId
My generate_series stops working (as I understand it, because I set certain conditions for generating a request) and instead of 30 lines, I get only 3.
How do I keep the ability to generate the theDate by month, with the ability to select specific IDs
I tried different options.
With this option:
FULL JOIN amortization_schedules asl on to_char(the_date, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 2 | 50000 | 2023-05-16 00:00:00.000000 |
| 3 | 0 | 2024-05-16 00:00:00.000000 |
In this case, it does not work correctly, since months are not generated and only three lines are displayed (if it is (the_date, 'yyyy-MM') = to_char(asl.date, 'yyyy-MM')).
If I change to (the_date, 'yyyy') = to_char(asl.date, 'yyyy') then the generation works, but it doesn't work correctly because it is year oriented.
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-07-16 00:00:00.000000 |
... ... ....
| 1 | 10000 | 2022-12-16 00:00:00.000000 |
| 2 | 50000 | 2023-01-16 00:00:00.000000 |
| 2 | 50000 | 2023-02-16 00:00:00.000000 |
| 2 | 50000 | 2023-03-16 00:00:00.000000 |
| 2 | 50000 | 2023-04-16 00:00:00.000000 |
... ... ....
| 3 | 0 | 2024-01-16 00:00:00.000000 |
but it should be:
| id | outstantandingprincipal | thedate |
-------------------------------------------------------------------
| 1 | 10000 | 2022-05-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-06-16 00:00:00.000000 |
| 1 | 10000 | 2022-07-16 00:00:00.000000 |
... ... ....
| 1 | 10000 | 2023-04-16 00:00:00.000000 |
| 2 | 50000 | 2023-05-16 00:00:00.000000 |
| 2 | 50000 | 2023-06-16 00:00:00.000000 |
| 2 | 50000 | 2023-07-16 00:00:00.000000 |
| 2 | 50000 | 2023-08-16 00:00:00.000000 |
... ... ....
| 3 | 0 | 2024-05-16 00:00:00.000000 |
| 3 | 0 | 2024-06-16 00:00:00.000000 |
| 3 | 0 | 2024-07-16 00:00:00.000000 |
I'm making a few intuitive leaps here, so if something looks off it might be because I don't have the entire picture.
From what I can tell you want the amortization schedule starting from the "date" for each ID and then going out a specific amount of time. I am guessing it is not truly the max date in that entire table, and that it varies by ID. In your example you went out one year, so for now I'm going with that.
You can use a generate_series inline, which will explode out each row. I believe something like this will give you the output you seek:
with schedule as (
select
id,
generate_series (date, date + interval '1 year', interval '1 month')::date as dt
from
amortization_schedules
)
select
asl.id, s.dt, asl.outstanding_principal
from
amortization_schedules asl
join schedule s on asl.id = s.id
JOIN tranches tr ON asl.tranche_id = tr.id
JOIN facilities f on tr.facility_id = f.id
WHERE
f.id = :id and
tr.tranche_number_id = :trancheNumberId
Is there another field that tells, by id, when the payments should end or one that will let us derive it (number of payments, payment end date, etc)?
One final note. If you use [left] outer joins and a where clause, as in below:
LEFT JOIN tranches tr ON asl.tranche_id = tr.id
LEFT JOIN facilities f on tr.facility_id = f.id
WHERE
f.id = :id and
tr.tranche_number_id = :trancheNumberId
You have effectively nullified the "left" and made these inner joins. In this case, get rid of "left," not because it will return wrong results but because it misleads. You are saying those fields must have those specific values, which means they must first exist. That's an inner join.
If you truly wanted these as left joins, this would have been more appropriate, but I don't think this is what you meant:
LEFT JOIN tranches tr ON
asl.tranche_id = tr.id and
tr.tranche_number_id = :trancheNumberId
LEFT JOIN facilities f on
tr.facility_id = f.id and
f.id = :id
I have one requirement where I need to find the first missing number in the ArrayList which is not there in Database(Oracle) column.
Scenario is like this :
Table 1:
From the above table I am thinking to make 3 lists
List<Integer> lst1 = new ArrayList<Integer>();
List<Integer> lst2 = new ArrayList<Integer>();
List<Integer> lst3 = new ArrayList<Integer>();
lst1 -> [0,1,2,3,4,5,6....1000]
lst2 -> [a0,a1,a2,a3,a4,a5,a6....a1000]
lst3 -> [b0,b1,b2,b3,b4,b5,b6....b1000]
As of now the lists contains approx 1000 values in the serial order.
Now I have a Database table as below
How can I match the lsts with Range column.
I need to find what value from lsts is not present in this table ?
Like in this case, if we see from the "lst1" the first available value which is not there in the table is "1" then next available value is "3" and then 5.6...so on.
Similarly for "lst2" the first missing value is "a3"
Is there any way to do ?
The below query obtains a prefix and numeric valuest of range_start and range_end.
For simplicity of examples I limited ranges to 0-5
SELECT lstname,
regexp_substr( rangestart, '[^0-9]') AS Prefix,
regexp_substr( rangestart, '[0-9]') AS r_start,
regexp_substr( rangeend, '[0-9]') AS r_end
FROM table_1
LSTNAME |PREFIX |R_START |R_END |
--------|-------|--------|------|
Lst1 | |0 |5 |
Lst2 |a |0 |5 |
Lst3 |b |0 |5 |
The below query will generate all values for ranges using the above query as a subquery.
Please note that CROSS JOIN LATERAL works on version Oracle 12c and later, if you are using earlier version then this query must be rewriten.
SELECT lstname, prefix || val AS val
FROM (
SELECT lstname,
regexp_substr( rangestart, '[^0-9]') AS Prefix,
regexp_substr( rangestart, '[0-9]') AS r_start,
regexp_substr( rangeend, '[0-9]') AS r_end
FROM table_1
) x
CROSS JOIN LATERAL (
SELECT LEVEL - 1 + x.r_start AS val
FROM dual
CONNECT BY LEVEL <= x.r_end - x.r_start + 1
)
LSTNAME |VAL |
--------|----|
Lst1 |0 |
Lst1 |1 |
Lst1 |2 |
Lst1 |3 |
Lst1 |4 |
Lst1 |5 |
Lst2 |a0 |
Lst2 |a1 |
Lst2 |a2 |
Lst2 |a3 |
Lst2 |a4 |
Lst2 |a5 |
Lst3 |b0 |
Lst3 |b1 |
Lst3 |b2 |
Lst3 |b3 |
Lst3 |b4 |
Lst3 |b5 |
And now say that table_2 contains the following values:
SELECT * FROM table_2
RANGE |
------|
3 |
a0 |
a1 |
a5 |
b3 |
b4 |
b5 |
To find missing values just LEFT JOIN the above queries to this table and filter out not null values.
Please note that I am using "RANGE" within quotes as a column name in this example because RANGE is reserved word in Oracle
SELECT lstname, val
FROM (
SELECT lstname, prefix || val AS val
FROM (
SELECT lstname,
regexp_substr( rangestart, '[^0-9]') AS Prefix,
regexp_substr( rangestart, '[0-9]') AS r_start,
regexp_substr( rangeend, '[0-9]') AS r_end
FROM table_1
) x
CROSS JOIN LATERAL (
SELECT LEVEL - 1 + x.r_start AS val
FROM dual
CONNECT BY LEVEL <= x.r_end - x.r_start + 1
)
) XX
LEFT JOIN table_2 t2
ON t2."RANGE" = xx.val
WHERE t2."RANGE" IS NULL
ORDER BY 1, 2;
LSTNAME |VAL |
--------|----|
Lst1 |0 |
Lst1 |1 |
Lst1 |2 |
Lst1 |4 |
Lst1 |5 |
Lst2 |a2 |
Lst2 |a3 |
Lst2 |a4 |
Lst3 |b0 |
Lst3 |b1 |
Lst3 |b2 |
This version of subquery emulates a lateral join and should work on Oracle 10, but I've not tested it
SELECT lstname,
prefix || column_value AS val
FROM (
SELECT lstname,
regexp_substr( rangestart, '[^0-9]') AS Prefix,
regexp_substr( rangestart, '[0-9]') AS r_start,
regexp_substr( rangeend, '[0-9]') AS r_end
FROM table_1
) x
CROSS JOIN table(cast(multiset(
SELECT LEVEL - 1 + x.r_start AS val
FROM dual
CONNECT BY LEVEL <= x.r_end - x.r_start + 1
) as sys.OdciNumberList)) q
;
I have two queries:
I run this query in one method
String query = "MATCH (g:Grid {name:'"+gridLocation+"'})<-[r:WILL_GO]-(t:Taxi)"
+ "WHERE r.reachedTime <= '"+userPickUp+"' RETURN t.name AS Taxi";
Result taxiWillGo = graphDb.execute(query);
And in another method
String query2 = "MATCH p=((g:Grid {name:'"+gridLocation+"'})-[r:TO*1..2]-(g2:Grid)), (g2)-[r2:LOCATION]-(t:Taxi) "
+ "WITH t, p, REDUCE(totalTime = 0, x IN RELATIONSHIPS(p) | totalTime + x.time) AS totalTime "
+ "WHERE totalTime <= 6 RETURN t.name as Taxi LIMIT 3";
Result taxiNeighbor = graphDb.execute(query2);
Both queries return the same element (a taxi), is it possible to merge both executed Results into a single one so at the end i have one "table" displaying all results.
WILL_GO Taxis:
+----------+
| Taxi |
+----------+
| "taxi 4" |
+----------+
NEIGHBOR Taxis:
+----------+
| Taxi |
+----------+
| "taxi 2" |
| "taxi 1" |
| "taxi 4" |
+----------+
e.g Merged Table WILL GO and NEIGHBOR
+----------+
| Taxi |
+----------+
| "taxi 2" |
| "taxi 1" |
| "taxi 4" |
+----------+
Thank you in advance!
You can use the UNION Cypher clause to combine the results of two queries: http://neo4j.com/docs/stable/query-union.html
MATCH ...
RETURN t
UNION
MATCH ...
RETURN t
I'm making a query to get the URIs of documents, that have a specific title. My query is:
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?document WHERE {
?document dc:title ?title.
FILTER (?title = "…" ).
}
where "…" is actually the value of this.getTitle(), since the query string is generated by:
String queryString = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
"PREFIX dc: <http://purl.org/dc/elements/1.1/> SELECT ?document WHERE { " +
"?document dc:title ?title." +
"FILTER (?title = \"" + this.getTitle() + "\" ). }";
With the query above, I get only the documents with titles exactly like this.getTitle(). Imagine this.getTitle is formed by more than 1 word. I'd like to get documents even if only one word forming this.getTitle appears on the document title (for example). How could I do that?
Let's say you've got some data like (in Turtle):
#prefix : <http://stackoverflow.com/q/20203733/1281433> .
#prefix dc: <http://purl.org/dc/elements/1.1/> .
:a dc:title "Great Gatsby" .
:b dc:title "Boring Gatsby" .
:c dc:title "Great Expectations" .
:d dc:title "The Great Muppet Caper" .
Then you can use a query like:
prefix : <http://stackoverflow.com/q/20203733/1281433>
prefix dc: <http://purl.org/dc/elements/1.1/>
select ?x ?title where {
# this is just in place of this.getTitle(). It provides a value for
# ?TITLE that is "Gatsby Strikes Again".
values ?TITLE { "Gatsby Strikes Again" }
# Select a thing and its title.
?x dc:title ?title .
# Then filter based on whether the ?title matches the result
# of replacing the strings in ?TITLE with "|", and matching
# case insensitively.
filter( regex( ?title, replace( ?TITLE, " ", "|" ), "i" ))
}
to get results like
------------------------
| x | title |
========================
| :b | "Boring Gatsby" |
| :a | "Great Gatsby" |
------------------------
What's particularly neat about this is that since you're generating the pattern on the fly, you could even make it based on another value from the graph pattern. For instance, if you want all pairs of things whose titles match on at least one word, you could do:
prefix : <http://stackoverflow.com/q/20203733/1281433>
prefix dc: <http://purl.org/dc/elements/1.1/>
select ?x ?xtitle ?y ?ytitle where {
?x dc:title ?xtitle .
?y dc:title ?ytitle .
filter( regex( ?xtitle, replace( ?ytitle, " ", "|" ), "i" ) && ?x != ?y )
}
order by ?x ?y
to get:
-----------------------------------------------------------------
| x | xtitle | y | ytitle |
=================================================================
| :a | "Great Gatsby" | :b | "Boring Gatsby" |
| :a | "Great Gatsby" | :c | "Great Expectations" |
| :a | "Great Gatsby" | :d | "The Great Muppet Caper" |
| :b | "Boring Gatsby" | :a | "Great Gatsby" |
| :c | "Great Expectations" | :a | "Great Gatsby" |
| :c | "Great Expectations" | :d | "The Great Muppet Caper" |
| :d | "The Great Muppet Caper" | :a | "Great Gatsby" |
| :d | "The Great Muppet Caper" | :c | "Great Expectations" |
-----------------------------------------------------------------
Of course, it's very important to note that you're pulling generating patterns based on your data now, and that means that someone who can put data into your system could put very expensive patterns in to bog down the query and cause a denial-of-service. On a more mundane note, you could run into trouble if any of your titles have characters in them that would interfere with the regular expressions. One interesting problem would be if something had a title with multiple spaces so that the pattern became The|Words|With||Two|Spaces, since the empty pattern in there might make everything match. This is an interesting approach, but it's got a lot of caveats.
In general, you could do this as shown here, or by generating the regular expression in code (where you can take care of escaping, etc.), or you could use a SPARQL engine that supports some text-based extensions (e.g., jena-text, which adds Apache Lucene or Apache Solr to Apache Jena).