Oracle Spatial Area Overlap Percentage - sql

I am working with 2 tables containing polygons as shown hereunder.
I have many parkingzones and two special areas contained in the dangerareas table.
Some of the parking zones overlap with these areas, my purpose is two know the extent of this overlap as an area overlap percentage per parking.
I want a null results for the not overlaping parkings.
I am working with SQL Oracle Developper.
I can't figure out why this query is not working.
(I have adapted the layer names as I can't use the real ones, I hope there is no typo left).
My query returns an error :
ORA-13011: value is out of range ORA-06512: at "MDSYS.SDO_GEOM", line
125 ORA-06512: at "MDSYS.SDO_GEOM", line 1878
13011. 00000 - "value is out of range"
db = CITY
Table 1 : parkingzones PK
id_parking
SHAPE
Table 2 : dangerareas DA
id_area
SHAPE
SELECT
(area_overlaps/area_zones*100) as pct_overlap
FROM
(SELECT(SDO_GEOM.SDO_AREA
(SDO_GEOM.SDO_INTERSECTION(PK.SHAPE,DA.SHAPE,0,05)0,05)) area_overlaps
FROM CITY.parkingzones PK
CITY.dangerareas DA),
(SELECT (SDO_GEOM.SDO_AREA(PK.SHAPES,0.05)) area_zones )
FROM CITY.parkingzones PK);

Total parking area:
select sum(SDO_GEOM.SDO_AREA(PK.SHAPES,0.05)) total_parking_area
FROM CITY.parkingzones PK
Total overlapping area:
SELECT sum(SDO_GEOM.SDO_AREA(
SDO_GEOM.SDO_INTERSECTION(PK.SHAPE,DA.SHAPE,0.05),0.05))) total_overlap
FROM CITY.parkingzones PK
,CITY.dangerareas DA
where sdo_anyinteract(PK.shape,DA.shape) = 'TRUE';
Overlap per parking (only that have overlap)
select (sdo_geom.sdo_area(sdo_geom.sdo_intersection(pk.shape, da.shape, 0.05), 0.05) /
sdo_geom.sdo_area(pk.shape, 0.05)) * 100 percentage
from city.parkingzones pk
,city.dangerareas da
where sdo_anyinteract(pk.shape, da.shape) = 'TRUE';

Related

how to divide by subset of table by itself, i.e. normalisation (t!=0 rows by t=0 rows)

I'm essentially looking for a way to normalise (t/t0) a bunch of measurements across timepoints with a specific timepoint, e.g. timepoint=0.
I have a table as following:
coordinate,timepoint,quantity
A1,0,50
B2,0,10
C3,0,60
A2,0,20
F1,0,20
A1,1,100
B2,1,150
C3,1,120
A2,1,140
F1,1,160
A1,4,100
B2,4,80
C3,4,80
A2,4,100
F1,4,120
I want to make a table that divides all the other non-zero timepoint rows by the 0 timepoint rows where the coordinates match, i.e. A1-t1 / A1-t0, A1-t4 / A1-t0, B2-t1 / B2-t0, B2-t0 / B2-t4 etc. etc. for wherever there is a join on coordinate available.
The result would be like:
coordinate,timepoint,quantity
A1,0,1
B2,0,1
C3,0,1
A2,0,1
F1,0,1
A1,1,2
B2,1,15
C3,1,2
A2,1,7
F1,1,8
etc.
Something like this mostly works...
select t0.coordinate,t0.quantity,tother.quantity,tother.quantity/t0.quantity as tnorm
(select * from table
where timepoint != 0) as tother
LEFT JOIN (select * from table
where timepoint = 0) as t0
ON (t1.coordinate = t2.coordinate);
Though I ideally would like to have a pivot of the table could be displayed where each column is each normalisation, e.g. columns as
coordinate, t0/t0, t1/t0, t4/t0 etc.
A1,1,2,value etc.
B2,1,15,value etc.
C3,1,2,value etc.
A2,1,7,value etc.
F1,1,8,value etc.
...though this might not be possible and must be done in postprocessing (e.g. pandas pivot).
I couldn't work out the right syntax for this one - any help is appreciated.
WITH t1 AS (
SELECT position, quantity
FROM table
WHERE timepoint = 0
)
SELECT t2.position, t2.timepoint, (t2.quantity/t1.quantity) quantity
FROM table t2
INNER JOIN t1 ON t2.position=t1.position

Defaulting missing data

I have a complex set of schema that I am trying to pull data out of for a report. The query for it joins a bunch of tables together and I am specifically looking to pull a subset of data where everything for it might be null. The original relations for the tables look as such.
Location.DeptFK
Dept.PK
Section.DeptFK
Subsection.SectionFK
Question.SubsectionFK
Answer.QuestionFK, SubmissionFK
Submission.PK, LocationFK
From here my problems begin to compound a little.
SELECT Section.StepNumber + '-' + Question.QuestionNumber AS QuestionNumberVar,
Question.Question,
Subsection.Name AS Subsection,
Section.Name AS Section,
SUM(CASE WHEN (Answer.Answer = 0) THEN 1 ELSE 0 END) AS NA,
SUM(CASE WHEN (Answer.Answer = 1) THEN 1 ELSE 0 END) AS AnsNo,
SUM(CASE WHEN (Answer.Answer = 2) THEN 1 ELSE 0 END) AS AnsYes,
(select count(distinct Location.Abbreviation) from Department inner join Plant on location.DepartmentFK = Department.PK WHERE(Department.Name = 'insertParameter'))
as total
FROM Department inner join
section on Department.PK = section.DepartmentFK inner JOIN
subsection on Subsection.SectionFK = Section.PK INNER JOIN
question on Question.SubsectionFK = Subsection.PK INNER JOIN
Answer on Answer.QuestionFK = question.PK inner JOIN
Submission on Submission.PK = Answer.SubmissionFK inner join
Location on Location.DepartmentFK = Department.PK AND Location.pk = Submission.PlantFK
WHERE (Department.Name = 'InsertParameter') AND (Submission.MonthTested = '1/1/2017')
GROUP BY Question.Question, QuestionNumberVar, Subsection.Name, Section.Name, Section.StepNumber
ORDER BY QuestionNumberVar;
There are 15 total locations, with this query I get 12. If I remove a relation in the join for Location I get 15 total locations but my answer data gets multiplied by 15. My issue is that not all locations are required to test at the same time so their answers should default to NA, They don't get records placed in the DB so the relationship between Location/Submission is absent.
I have a workaround almost in place via the select count distinct but, The second part is a query for finding what each location answered instead of a sum which brings the problem right back around. It also has to be dynamic because the input parameters for a department won't bring a static number of locations back each time.
I am still learning my SQL so any additional material to look at for building this query would also be appreciated. So I guess the big question here is, How would I go about creating default data in this query for anytime the Location/Submission relation has a null value?
Edit: Dummy Data
QuestionNumberVar | Section | Subsection | Question | AnsYes | AnsNo | NA (expected)
1-1.1 Math Algebra Did you do your homework? 10 1 1(4)
1-1.2 Math Algebra Did your dog eat it? 9 3 0(3)
2-1.1 English Greek Did you do your homework? 8 0 4(7)
I have tried making left joins at various applicable portions of the code to no avail. All attempts at left joins have ended with no effect on info output. This query feeds into the Dataset for an SSRS report. There are a couple workarounds for this particular section via an expression to take total Locations and subtract AnsYes and AnsNo to get the true NA value but as explained above doesn't help with my next query.
Edit: SQL Server 2012 for those who asked
Edit: my attempt at an isnull() on the missing data returns nothing I suspect because the query already eliminates the "null/missing" data. Left joining while doing this has also failed. The point of failure is on Submissions. if we bind it to Locations there are locations missing but if we don't bind it there are multiplied duplicates because Department has a One-To-Many with Location and not vice versa. I am unable to make any schema changes to improve this process.
There is a previous report that I am trying to emulate/update. It used C# logic to process data and run multiple queries to attain the same data. I don't have this luxury. (previous report exports to excel directly instead of SSRS). Here is the previous logic used.
select PK from Department where Name = 'InsertParameter';
select PK from Submission where LocationFK = 'Location.PK_var' and MonthTested = '1/1/2017'
Then it runs those into a loop where it processes nulls into NA using C# logic
EDIT (Mediocre Solution): I ended up doing the workaround of making a calculated field that subtracts Yes and No from the total # of Locations that have that Dept. This is a mediocre solution because I didn't solve my original problem and made 3 datasets that should have been displayed as a singular dataset. One for question info, one for each locations answer and one for locations that didnt participate. If a true answer comes up I will check its validity but for now, Problem psuedo solved.

Assign custom labels to sql query

This is a pretty simple question, I think, but I cannot find an answer for it here, or through online searching.
I am running a count query, and am having trouble with value labels and column headings
Query is
SELECT
mail_code AS "code",
COUNT(mail_code) AS "count"
FROM data.base
GROUP BY mail_code
;
I get back:
C Count
- -----
Y 110
X 785
Z 92
Questions:
How do I get the first variable (code) to display its full name, instead of a single letter?
How do I change Y, X, and Z to read "phone," "mail," and "email" ...or anything else for that matter?
The length of the mail_code variable is 1 byte...is that why only the first letter is showing up as my varname?
...I was initially warned that based on my title, it might get downvoted. OK, but I tried to look elsewhere for the answer and could not find it, IE I tried due diligence.
Thank you in advance.
Mostly, they keep a look-up table with foreign - primary key relationship. i.e. write explanation in a table with code_name and explanation columns
with values X , phone ; Y, mail : Z , email respectively,
and Join them with a SQL statement :
select d.mail_code as "code", c.explanation as "communication type", count(1) as "count"
from data_base d inner join codes c on ( d.mail_code = c.mail_code )
group by d.mail_code, c.explanation;
Where DDLs are as following to create tables :
create table codes(mail_code varchar2(1) primary key,explanation varchar2(15));
create table data_base( mail_code varchar2(1) references codes(mail_code));
Demo
Thank you all, I got about 75% of the way there.
Syntax:
COLUMN mail_code FORMAT A10
SELECT
DECODE (mail_code,
'X', 'mail',
'Y', 'phon',
'Z', 'emai') AS "code",
COUNT(*) AS "count"
FROM data.base
GROUP BY mail_code
;
Returned
code count
---- -----
mail 110
phon 785
emai 92
Double quotes are needed b/c Oracle. It'll work to change name to 'code' wo double quotes but comes up as all caps (CODE) wo them, and I can choose w double quotes.
Thank you all for your help with this!

I am trying to write a SQL nested query that finds/uses a max value to find the entry just before the max value

I am fairly new to SQL and am trying to write a query that finds the last time a water meter was read so I can see the value. There is a table of properties that have meters and another table of meters that stores the inputs from engineers. Every input is listed as a sequence, a keyword lists the type of input and expression lists their entry. The max sequence will not always be the answer.
What I am looking for is the last time the read the meter for water and then also get the value for electricity from that reading which is stored in the previous entry (sequence). To make it harder engineers input the sequence number and some go by ones (1,2,3) and others go by twos (2,4,6) so the previous entry may be minus one or maybe minus two.
I can write the queries to find the max sequence and another one to find the entry one previous or two previous but cannot figure out how to make it into one query.
to find the max sequence for site 12345, I have:
SELECT MAX(M.SEQUENCE) maxseq
FROM METERS M JOIN PROPERTY P ON M.PROPNUM = P.PROPNUM
WHERE (P.CORP_ID ='12345' AND M.KEYWORD = 'WTR')
I manually search for the entry before to get the electricity entry with the following query.
SELECT P.NAME, P.CORP_ID, M.KEYWORD, M.SEQUENCE, M.EXPRESSION
FROM METERS M JOIN PROPERTY P ON M.PROPNUM = P.PROPNUM
WHERE (P.CORP_ID ='12345')
ORDER BY M.SEQUENCE
I have tried different nested queries but have not been able to write anything that will work.
The data that I am interested in for the meters table looks like:
PROPNUM SEQUENCE KEYWORD EXPRESSION
10a124 95 ELC 9845
10a124 96 WTR 4521
10a124 97 SVC A105
10a124 98 HEALTH GOOD
10a124 99 DAY 150209
10a124 100 HEALTH GOOD
10a124 101 ELC 10283
10a124 102 WTR 4621
I use the property table to find the PROPNUM for the site I am interested as I have the site's ID (CORP_ID) but not its PROPNUM value.
The result I would like to get back would look like below.
NAME WTR_EXPRESSION ELC_EXPRESSION
SMITH 4621 10283
You can inner join the METER table to the PROPERTY table once for each KEYWORD, and specify that the SEQUENCE for 'ELC' (guessing KEYWORD) is less than the 'WTR' SEQUENCE. Since you are on SQL SERVER, we can do this in a CTE and inner join that data set to the METER table to display the EXPRESSION values for each KEYWORD in a single row:
;with wtr_elc as (
select
p.PROPNUM,
p.NAME,
max(w.SEQUENCE) as max_wtr_seq,
max(e.SEQUENCE) as max_elc_seq
from PROPERTY as p
inner join METERS as w
on w.PROPNUM = p.PROPNUM
w.KEYWORD = 'WTR'
inner join METERS as e
on e.PROPNUM = p.PROPNUM
and e.KEYWORD = 'ELC'
and e.SEQUENCE < w.SEQUENCE
where p.CORP_ID ='12345'
group by
p.PROPNUM,
p.NAME)
select
wtr_elc.NAME,
wtr.EXPRESSION as WTR_EXPRESSION,
elc.EXPRESSION as ELC_EXPRESSION
from METERS as wtr
inner join wtr_elc
on wtr_elc.PROPNUM = wtr.PROPNUM
and wtr_elc.max_wtr_seq = wtr.SEQUENCE
inner join METERS elc
on wtr_elc.PROPNUM = elc.PROPNUM
and wtr_elc.max_elc_seq = elc.SEQUENCE
and elc.KEYWORD = 'ELC'
where wtr.KEYWORD = 'WTR'
If you want to do this for more or all PROPERTY records, you can modify the where clause in the CTE.

How do I swap values of records in Oracle SQL?

I'm supposed to flip flight numbers for pairs of flights going back and forth from a set of cities, so for example:
1439 ATL SFO
1440 SFO ATL
would end up as:
1440 ATL SFO
1439 SFO ATL
I tried this query (because you can't UPDATE .. JOIN in Oracle):
UPDATE
(SELECT f.airline, f.flightno flightno_f, d.airline, d.flightno flightno_d
FROM flights f
INNER JOIN flights d ON f.airline = 9 AND
f.sourceairport = d.destairport AND
f.destairport = d.sourceairport AND d.airline = 9
WHERE d.flightno < f.flightno) g
SET g.flightno_f = g.flightno_d,
g.flightno_d = g.flightno_f;
where airline, flightno is the primary key for table flights. The select gives me the correct set of records that I want to swap on, but the UPDATE... SET gives me this error:
SET g.flightno_f = g.flightno_d,
*
ERROR at line 7:
ORA-01779: cannot modify a column which maps to a non key-preserved table
Any ideas on where I'm going wrong?
In order to update a join, it doesn't matter that the dataset you are selecting happens to be effectively key-preserved; Oracle has to be able to see from the constraints and the predicates that it will by definition be key-preserved. And because you have an inequality condition on the flightnumber, there is nothing in the data definition to guarantee that you won't have multiple matches for a given source row.
If it's guaranteed that the flight numbers will always differ by 1, you might be able to use the join method if you change the condition to d.flightno + 1 = f.flightno.
In any case, I think the following will work ... because of statement-level read-consistency the subquery should return correct results even as the rows are updated.
UPDATE flights f1
SET flightno =
(SELECT flightno
FROM flights f2
WHERE f2.airline = f1.airline
AND f2.sourceairport = f1.destairport
AND f2.destairport = f1.sourceairport
)
WHERE airline = 9;