How to join on spatial functions in Peewee? - postgis

I'm struggling with figuring out how to join a table in Peewee based on a fairly common table postgis pattern. I need to join a table based on a postgis function (st_contains). I imagine it would look something like:
Station.select().join(Location, on=fn.ST_Intersects(Station.geom, Location.geom)).where(Location.name == 'Ravenswood')
The above query, if supported, would return all Stations in the Location named Ravenswood. The equivalent SQL would be:
SELECT station.name, station.district, station.line
FROM station INNER JOIN location ON ST_Intersects(station.geom, loc.geom)
WHERE location.name = 'Ravenswood';
Unfortunately my experiments all seem to end with this abbreviated traceback:
File "/Users/j.../python2.7/site-packages/peewee.py", line 1555, in generate_joins
left_field = field.to_field
AttributeError: 'NoneType' object has no attribute 'to_field'
Does peewee support this? I can't find it in the documentation.

Peewee's join generation checked for expressions but not function calls. I have fixed this bug in 61034c5 (released in v2.6.1). If you use peewee master you should be able to run the query now.

Related

Django model based on an SQL table-valued function using MyModel.objects.raw()

If it's relevant I'm using Django with Django Rest Framework, django-mssql-backend and pyodbc
I am building some read only models of a legacy database using fairly complex queries and Django's MyModel.objects.raw() functionality. Initially I was executing the query as a Select query which was working well, however I received a request to try and do the same thing but with a table-valued function from within the database.
Executing this:
MyModel.objects.raw(select * from dbo.f_mytablefunction)
Gives the error: Invalid object name 'myapp_mymodel'.
Looking deeper into the local variables at time of error it looks like this SQL is generated:
'SELECT [myapp_mymodel].[Field1], '
'[myapp_mymodel].[Field2] FROM '
'[myapp_mymodel] WHERE '
'[myapp_mymodel].[Field1] = %s'
The model itself is mapped properly to the query as executing the equivalent:
MyModel.objects.raw(select * from dbo.mytable)
Returns data as expected, and dbo.f_mytablefunction is defined as:
CREATE FUNCTION dbo.f_mytablefunction
(
#param1 = NULL etc etc
)
RETURNS TABLE
AS
RETURN
(
SELECT
field1, field2 etc etc
FROM
dbo.mytable
)
If anyone has any explanation as to why these two modes of operation are treated substantially differently then I would be very pleased to find out.
Guess you've figured this out by now (see docs):
MyModel.objects.raw('select * from dbo.f_mytablefunction(%s)', [1])
If you'd like to map your table valued function to a model, this gist has a quite thorough approach, though no license is mentioned.
Once you've pointed your model 'objects' to the new TableFunctionManager and added the 'function_args' OrderedDict (see tests in gist), you can query it as follows:
MyModel.objects.all().table_function(param1=1)
For anyone wondering about use cases for table valued functions, try searching for 'your_db_vendor tvf'.

Why Hibernate HSQL Concat is not working for MSSQL?

So, I have Hibernate 5.3.1 in a project which connects to different enginees (MySql, Oracle, PostgreSQL and MS SQL), so I can't use native queries.
Let's say I have 3 records in a table, which all of them have the same datetime, but I need to group them only by date (not time). For example, 2019-12-04;
I execute this query:
SELECT
CONCAT(year(tx.date_), month(tx.date_), day(tx.date_)),
iss.code,
COUNT(tx.id)
FROM
tx_ tx
JOIN
issuer_ iss
ON
tx.id_issuer = iss.id
GROUP BY
CONCAT(year(tx.date_), month(tx.date_), day(tx.date_)), iss.code
But, when I test it connected to SQL SERVER 2017, instead of return 20191204, it's returning 2035. In Oracle and MySQL is working fine.
Anyone has any idea why is this happen? I've tried different ways, like use + instead of CONCAT but the result is the same.
I've also tried to extract them for separate (without concat), and they have been returning correct. The problem is, I need to group them by the complete date.
And just for the record, the field is declared as datetime2 in DDBB
How about simply adding them, instead of using CONCAT.
(year(tx.date_)*10000 + month(tx.date_)*100 + day(tx.date_)*1) AS datenum
Thus, try this:
SELECT
CAST((year(tx.date_)*10000 + month(tx.date_)*100 + day(tx.date_)*1) AS string) AS datenum,
iss.code
FROM tx_ tx
JOIN issuer_ iss
ON tx.id_issuer = iss.id
GROUP BY year(tx.date_), month(tx.date_), day(tx.date_), iss.code
Thanks for the hint Gert Arnold gave me. I just didn't realize that the query was adding like if they were numbers in MSSQL.
Finally, I manage to make it work in the 4 RDBMS casting to string first
SELECT
CONCAT(CAST(year(tx.date_) AS string), CAST(month(tx.date_) AS string), CAST(day(tx.date_) AS string)),
iss.code
FROM
tx_ tx
JOIN
issuer_ iss
ON
tx.id_issuer = iss.id
GROUP BY
CONCAT(year(tx.date_), month(tx.date_), day(tx.date_)), iss.code
I tried also casting to TEXT, but it throws exception in MySQL
Why use concat() to begin with?
Assuming Hibernate takes care of converting the non-standard year(), month() and day() functions, then the following should work on any DBMS
SELECT year(tx.date_), month(tx.date_), day(tx.date_), iss.code
FROM tx_ tx
JOIN issuer_ iss ON tx.id_issuer = iss.id
GROUP BY year(tx.date_), month(tx.date_), day(tx.date_), iss.code

How to get the result of CONNECT_BY_ISCYCLE and CONNECT_BY_ISLEAF in snowflake without using them?

I need to make hierarchical queries, and I need to get the results of CONNECT_BY_ISCYCLE and CONNECT_BY_ISLEAF, but these features are supported in Oracle not in Snowflake.
What are the alternative ways to implement the functionalities of CONNECT_BY_ISCYCLE and CONNECT_BY_ISLEAF in snowflake without using them as these keywords are not supported there?
Wonder if you have taken a look at the following Snowflake features?
https://docs.snowflake.net/manuals/user-guide/queries-hierarchical.html#using-connect-by-or-recursive-ctes-to-query-hierarchical-data
Yes I took a look there. I also took a look at https://docs.snowflake.net/manuals/sql-reference/constructs/connect-by.html where it clearly says that these features are not supported in Snowflake.
I was trying below code block to find an alternative but facing varieties of error in snowflake.
person_vertex as (
select
emp_number,
user_id
from person
),
person_edges as (
select
supervisor_emp_number,
emp_number
from person
where supervisor_emp_number is not null
),
select
pv.emp_number emp_id_pk,
level,
CONNECT_BY_ROOT pv.emp_number AS root,
concat(SYS_CONNECT_BY_PATH(pv.emp_number,':'),':') as path,
-- CONNECT_BY_ISCYCLE AS iscyclic, ------------------- no idea how to implement this
-- CONNECT_BY_ISLEAF as isleaf ------------------- i tried below block, but it is not working
case
when (pe.supervisor_emp_number in (select emp_number from pv)) then 0
else 1
end AS isleaf
from person_vertex pv
left join person_edges pe on pv.emp_number = pe.emp_number
connect by prior A.emp_number = A.supervisor_emp_number
start with A.supervisor_emp_number is null
Any help with this block is really appreciated.
Thanks.
enter code here

How table was created in SQL-Server

What I need to find is the procedure of recreating some table, what data sources were used, which scripts if any &c. So is it possible to differentiate somehow, maybe in system views or similar, if the table was created manually or by query and if the data was imported from external data or from already existing table/view in the database? I already know who created and when. I’ve pretty much screened whole database without results and now I am looking for hints in metadata.
If the table was created recently, you can glean information from the default trace. The query below will list object created and altered events. The default trace is a rollover trace so forensic information will be limited based on activity.
SELECT
trace.DatabaseName
,trace.ObjectName
,te.name AS EventName
,tsv.subclass_name
,trace.EventClass
,trace.EventSubClass
,trace.StartTime
,trace.EndTime
,trace.NTDomainName
,trace.NTUserName
,trace.HostName
,trace.ApplicationName
,trace.Spid
FROM (SELECT REVERSE(STUFF(REVERSE(path), 1, CHARINDEX(N'\', REVERSE(path)), '')) + N'\Log.trc' AS path
FROM sys.traces WHERE is_default = 1) AS default_trace_path
CROSS APPLY fn_trace_gettable(default_trace_path.path, DEFAULT) AS trace
JOIN sys.trace_events AS te ON
trace.EventClass=te.trace_event_id
JOIN sys.trace_subclass_values AS tsv ON
tsv.trace_event_id = EventClass
AND tsv.subclass_value = trace.EventSubClass
WHERE te.name IN(N'Object:Altered', N'Object:Created')
AND tsv.subclass_name = 'Commit'
ORDER BY trace.StartTime;

How to create VIEW in MS Access Database using Delphi Application without installing MSAccess on PC?

I want to create VIEW definitions on MS Access. I have used following CREATE VIEW Statement:
SELECT
MFP.FollowUpPlan_Id,
MFP.FollowUpPlan_Name AS PlanName,
DFP.Sequence_No AS SequenceNo,
MFS.FollowUpSchedule_Name AS ScheduleName
FROM
MAS_FollowUp_Plan AS MFP,
DET_FollowUp_Plan AS DFP,
MAS_FollowUp_Schedule AS MFS
WHERE
(((MFP.FollowUpPlan_Id)=DFP.FollowUpPlan_Id) AND
((DFP.FollowUpSchedule_Id)=MFS.FollowUpSchedule_Id)) AND
MFP.is_Deleted = FALSE AND
DFP.is_Deleted = false
ORDER BY
MFP.FollowUpPlan_Id, DFP.Sequence_No;
but it throw an error:
Only Simple Select Queries are allowed in view.
Please Help, Thanks in Advance.
The issue here, as Jeroen explained, is a limitation of Access' CREATE VIEW statement. For this case, you can use CREATE PROCEDURE instead. It will create a new member of the db's QueryDefs collection --- so from the Access user interface will appear as a new named query.
The following statement worked for me using ADO from VBScript. From previous Delphi questions on here, my understanding is that Delphi can also use ADO, so I believe this should work for you, too.
CREATE PROCEDURE ViewSubstitute AS
SELECT
MFP.FollowUpPlan_Id,
MFP.FollowUpPlan_Name AS PlanName,
DFP.Sequence_No AS SequenceNo,
MFS.FollowUpSchedule_Name AS ScheduleName
FROM
(MAS_FollowUp_Plan AS MFP
INNER JOIN DET_FollowUp_Plan AS DFP
ON MFP.FollowUpPlan_Id = DFP.FollowUpPlan_Id)
INNER JOIN MAS_FollowUp_Schedule AS MFS
ON DFP.FollowUpSchedule_Id = MFS.FollowUpSchedule_Id
WHERE
MFP.is_Deleted=False AND DFP.is_Deleted=False
ORDER BY
MFP.FollowUpPlan_Id,
DFP.Sequence_No;
You cannot mix ORDER BY with JOIN when creating views in Access. It will get you the error "Only simple SELECT queries are allowed in VIEWS." (note the plural VIEWS)
Having multiple tables in the FROM is a kind of to JOIN.
either remove the ORDER BY,
or have only one table in the FROM and no JOINs.
I remember from the past (when I did more Access stuff than now) seeing this for a large query with a single table select with an ORDER BY as well.
The consensus is that you should not have ORDER BY in views anyway, so that is your best thing to do.
Another reason that you can get the same error message is if you add parameters or sub selects. Access does not like those in views either, but that is not the case in your view.
Declare variable olevarCatalog ,cmd as OleVariant in Delphi, Uses ComObj
olevarCatalog := CreateOleObject('ADOX.Catalog');
olevarCatalog.create(YourConnectionString); //This Will create MDB file.
// Using ADO Query(CREATE TABLE TABLEName....) add the required Tables.
// To Insert View Definition on MDB file.
cmd := CreateOleObject('ADODB.Command');
cmd.CommandType := cmdText;
cmd.CommandText := 'ANY Kind of SELECT Query(JOIN, OrderBy is also allowed)';
olevarCatalog.Views.Append('Name of View',cmd);
cmd := null;
This is a best way to Create MS ACCESS File(.MDB) and VIEWs using Delphi.

Resources