Turning line segments in to arcs, pretty transport display - line

I am trying to turn a line segment display
in to arc segment display
My linput data is encoded as a series of MultiLineString geometries,
I have tried things like st_forcecurve(st_linemerge(geom)) which turns my data in to series of very nice straight lines encoded as CompoundCurve geometries. Its there any way to acutal generate a curved line instead off a straight line.

It seems I am not the only one that has run in to issues like this A very similair problem
One of the answers did what I wanted. I just converted that answer in to an psql function that generates a curved arc, using a factor for how curved you want the answer, low values very curvy, high values a stright line.
--
-- Draw an arc geom between two differentlocations
--
-- point1 must be a point starting location
-- point2 must be a point ending location
-- arc_fractor how curved you want the result
-- returns a curved geometry
create or replace function mk_arg_geom(point1 geometry, point2 geometry, arc_factor integer )
returns geometry
as $$
DECLARE
ret geometry;
ret_srid integer;
tmp_geom geometry;
c_geom geometry;
BEGIN
ret := null;
ret_srid:=st_srid(point1);
tmp_geom=st_makeline(point1,point2);
c_geom:= ST_CurveToLine('CIRCULARSTRING(' ||
st_x(st_startpoint(tmp_geom)) ||
' ' ||
st_y(st_startpoint(tmp_geom)) ||
', ' ||
st_x(st_centroid(ST_OffsetCurve(tmp_geom, st_length(tmp_geom)/arc_factor, 'quad_segs=4 join=bevel'))) ||
' ' ||
st_y(st_centroid(ST_OffsetCurve(tmp_geom, st_length(tmp_geom)/arc_factor, 'quad_segs=4 join=bevel'))) ||
', ' ||
st_x(st_endpoint(tmp_geom)) ||
' ' ||
st_y(st_endpoint(tmp_geom))
|| ')');
ret=st_setsrid(c_geom,ret_srid);
return ret;
END;
$$
LANGUAGE plpgsql;

Related

PL/SQL Query to check row's length

I'm having some trouble on solving this.
I have to check on the table, if there's any row that exceed the length of 34 characters (for this, I'm using this first part of the query using Lenght command), if found, return the error with the variable 'END_CNPJ$' (that's being populated by the second part of the query 'ENDERECO') so the user can see which row has more than 34 characters. Is this code correct (probably not)? If it isn't, how can I fix it?
SELECT
LENGTH(CONCAT (CONCAT (CONCAT(CONCAT (CONCAT (CONCAT (CONCAT (
'', T.TTIPO_LOGR),
''), T.TENDERE),
''), T.NNRO_ENDER),
''),T.TCOMPL_ENDER) ),
T.TTIPO_LOGR || ' ' || T.TENDERE || ', ' || T.NNRO_ENDER || ' ' || T.TCOMPL_ENDER || ' - ' || TMUNICI || ' CNPJ: ' || T.NCGC AS ENDERECO
INTO CHARACTER_COUNT$, END_CNPJ$
FROM TBENDER T
WHERE T.CEMPRES = :ENDER_BLK.CEMPRES;
IF CHARACTER_COUNT$ > 34 THEN
MSG_ALERT_COSMO(' You exceeded 34 character for this address: ' || END_CNPJ$ );
RAISE FORM_TRIGGER_FAILURE;
END IF;
I hope I'm not violating any rule, just got here yesterday :D
TIA
That's actually Oracle Forms, is it not? raise form_trigger_failure and :ender_blk smell so.
In that case, the only drawback might be possibility of no rows in that table for block item value (which will raise no_data_found) or two or more rows for it (which will then raise too_many_rows).
Other than that, this should be OK.
Though, it is kind of unclear why you nicely concatenated values (using the double pipe || operator) for END_CNPJ$ and nested that many concat functions for CHARACTER_COUNT$.
Also, you didn't post the whole trigger code (missing declarations, begin-end keywords, perhaps something else).
But, as I said, in general - that's OK.

How do I parameterize table & column in a Postgres-custom-function, selecting PK if value exists, otherwise insert it and return PK anyways?

Trying to do what I specified in the title, I already got the upsert-functionalities working, however when I try to parameterize it, I'm just out of my depth and can't debug it.
My query:
CREATE OR REPLACE FUNCTION custom_upsert(target_value_input text,
target_table_input text,
target_column_input text,
OUT pk_output int)
LANGUAGE plpgsql AS
$func$
BEGIN
LOOP
execute 'SELECT id '
' FROM ' || target_table_input ||
' WHERE ' || target_column_input || ' = ' || target_value_input ||
' INTO pk_output';
EXIT WHEN FOUND;
execute 'INSERT INTO ' || target_table_input || 'AS o ( ' || target_column_input || ' )'
' VALUES ( ' || target_value_input || ' ) '
' ON CONFLICT ( ' || target_column_input || ' ) DO NOTHING '
' RETURNING o.id'
' INTO pk_output';
EXIT WHEN FOUND;
END LOOP;
END
$func$;
now when I try to use the function, I get:
ERROR: syntax error at or near "INTO"
LINE 1: ...module WHERE artifact_id = artifact_id_example_1 INTO pk_ou...
^
QUERY: SELECT id FROM maven_module WHERE artifact_id = artifact_id_example_1 INTO pk_output
CONTEXT: PL/pgSQL function custom_upsert(text,text,text) line 4 at EXECUTE
What puzzles me about this is the fact that this syntax works fine in an unparameterized version:
https://dbfiddle.uk/?rdbms=postgres_14&fiddle=765389a746d3a392bc646fbedb7ed3b3
My attempts at parameterization:
https://dbfiddle.uk/?rdbms=postgres_14&fiddle=1bffab45d8a9587342a7c3253ea35fc8
https://dbfiddle.uk/?rdbms=postgres_14&fiddle=de6ba235aa21dae33b922f8fddac3b63
Thank you very much in advance, first time posting so if there's anything I should do differently when asking a question, I'm happy about feedback
edit: this is my function call:
-- should return pk of existing artifact_id
SELECT custom_upsert('artifact_id_example_1', 'maven_module', 'artifact_id');
-- should return pk of new artifact_id
SELECT custom_upsert('artifact_id_example_2', 'maven_module', 'artifact_id');
Do not concatenate strings like that. The function format() makes your life much easier (safer), e.g.
EXECUTE format('INSERT INTO %1$I AS o (%2$I)
VALUES (%3$L) ON CONFLICT (%2$I) DO NOTHING RETURNING o.id',
target_table_input,
target_column_input,
target_value_input) INTO pk_output;
%I will wrap the identifiers with double quote, which is handy when tables or columns are case sensitive of contain special characters.
%L will wrap the literals with single quotes
1$, 2$ and 3$ are the variables positions provided in the format() call, which is quite handy if one variable is used more than once.
Demo: db<>fiddle

How get all matching positions in a string?

I have a column flag_acumu in a table in PostgreSQL with values like:
'SSNSSNNNNNNNNNNNNNNNNNNNNNNNNNNNNSNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN'
I need to show all positions with an 'S'. With this code, I only get the first such position, but not the later ones.
SELECT codn_conce, flag_acumu, position('S' IN flag_acumu) AS the_pos
FROM dh12
WHERE position('S' IN flag_acumu) != 0
ORDER BY the_pos ASC;
How to get all of them?
In Postgres 9.4 or later you can conveniently use unnest() in combination with WITH ORDINALITY:
SELECT *
FROM dh12 d
JOIN unnest(string_to_array(d.flag_acumu, NULL))
WITH ORDINALITY u(elem, the_pos) ON u.elem = 'S'
WHERE d.flag_acumu LIKE '%S%' -- optional, see below
ORDER BY d.codn_conce, u.the_pos;
This returns one row per match.
WHERE d.flag_acumu LIKE '%S%' is optional to quickly eliminate source rows without any matches. Pays if there are more than a few such rows.
Detailed explanation and alternatives for older versions:
PostgreSQL unnest() with element number
Since you didn't specify your needs to a point in which one could answer properly, I'm going with my assumption that you want a list of positions of occurence of a substring (can be more than 1 character long).
Here's the function to do that using:
FOR .. LOOP control structure,
function substr(text, int, int).
CREATE OR REPLACE FUNCTION get_all_positions_of_substring(text, text)
RETURNS text
STABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
output_text TEXT := '';
BEGIN
FOR i IN 1..length($1)
LOOP
IF substr($1, i, length($2)) = $2 THEN
output_text := CONCAT(output_text, ';', i);
END IF;
END LOOP;
-- Remove first semicolon
output_text := substr(output_text, 2, length(output_text));
RETURN output_text;
END;
$$;
Sample call and output
postgres=# select * from get_all_positions_of_substring('soklesocmxsoso','so');
get_all_positions_of_substring
--------------------------------
1;6;11;13
This works too. And a bit faster I think.
create or replace function findAllposition(_pat varchar, _tar varchar)
returns int[] as
$body$
declare _poslist int[]; _pos int;
begin
_pos := position(_pat in _tar);
while (_pos>0)
loop
if array_length(_poslist,1) is null then
_poslist := _poslist || (_pos);
else
_poslist := _poslist || (_pos + _poslist[array_length(_poslist,1)] + 1);
end if;
_tar := substr(_tar, _pos + 1, length(_tar));
_pos := position(_pat in _tar);
end loop;
return _poslist;
end;
$body$
language plpgsql;
Will return a position list which is an int array.
{position1, position2, position3, etc.}

Formatting Oracle SQL column with non-standard format

I have an Oracle database where a file location is stored. Unfortunately, it isn't properly formatted.
For example, the file location is C:\images\00\45\34\34.IMG and is stored in the database as: 00453434.
I am able to use CONCAT to put C:\images and .IMG around the column, but I can't format the actual location to put \s in.
I've tried to_char, and to_number but it requires a specified format.
(My crappy attempt: to_char(filename, '09"\"09"\"09"\"09'))
Is there any way in SQL to format freely?
One method... assuming fixed length of each segment meaning each path is 2 digits including file name.
select 'C:\images\'|| substr('00453434',1,2) || '\' ||
substr('00453434',3,2) || '\' ||
substr('00453434',5,2) || '\' ||
substr('00453434',7,2) || '.IMG' as fullPath from dual
If needed at multiple queries, creating a PL/SQL function can also solve your problem. This example also assumes that each path has 2 digits, but supports paths of different lengths:
CREATE OR REPLACE FUNCTION GET_FILENAME(ID IN VARCHAR2, PREFIX IN VARCHAR2, SUFFIX IN VARCHAR2) RETURN VARCHAR2 IS
i PLS_INTEGER;
r VARCHAR2(4000);
BEGIN
r := PREFIX;
FOR i IN 1..LENGTH(ID)/2 LOOP
r := r || '\' || SUBSTR(ID, 2*i-1, 2);
END LOOP;
RETURN r || SUFFIX;
END;
/
The function can then be used within your standard SQL queries (or view definitions) as follows:
WITH TA_FILES AS (
SELECT '12345678' AS ID FROM DUAL
)
SELECT GET_FILENAME(ID, 'C:\images', '.IMG') FROM TA_FILES

How to always show two decimal places on a number in PL/SQL

Folks,
I have a existing stored procedure that I'm trying to update to always show two decimal places even when it's a whole number or single decimal number.
This stored procedure builds out a message that has to show v_credit_amt as a two decimal number, yet the value assigned to v_credit_amt can be either a whole number or single decimal or a two decimal value
i.e. 175 should display as 175.00, 250.5 should display as 250.50, 395.95 should display as 395.95.
Here is the relevant stock pl/sql
v_credit_amt number(22,2);
select NVL(sit_credit_limit,0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
I thought that formatting via to_char during the select would solve this, ala
select NVL(to_char(sit_credit_limit, 'fm99999.00'),0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
--v_credit_amt := to_char(v_credit_amt, 'fm99999.00');
insert into SQL_TXT values (v_credit_amt);
commit;
As you can see from the commented out line above, I've also tried it once the v_credit_amt variable is defined
I've also tried it with the to_number function
select NVL(to_number(sit_credit_limit, '99999.00'),0)
into v_credit_amt
from sites
where sit_pkey = p_site_id;
However if the value stored in the sit_credit_limit column is a whole number without decimals, or a number like 250.5 v_credit_amt shows the same in all cases when querying SQL_TXT table I'm inserting to for debugging.
SQL> select * from SQL_TXT;
COL1
--------------------------------------------------------------------------------
175
250.5
This particular event simply concatenates multiple message portions into a single long message string that is returned i.e.
if p_message_id = '14' then
v_message := 'Comtrol Check In Message Posted';
v_string := v_string || '008' || lpad(length(v_fname || ' ' || v_lname), 3, '0') || v_fname || ' ' || v_lname;
v_string := v_string || '059' || lpad(1, 3, '0') || '0';
--v_string := v_string || '089' || lpad(1, 3, v_credit_amt) || to_char(v_credit_amt);
v_string := v_string || '089' || lpad(length(to_char(v_credit_amt, 'fm9999999.00')), 3, '0') || to_char(v_credit_amt, 'fm9999999.00');
v_string := v_string || '106' || lpad(length(nvl(v_acct_number,'')), 3, '0') || v_acct_number;
--v_string := v_string || '106' || v_acct_number;
v_string := v_string || '164' || lpad(1, 3, '0') || '0';
v_string := v_string || '174' || lpad(length(v_rm_phone_num), 3, '0') || v_rm_phone_num;
v_string := v_string || '175' || lpad(length(v_rm_id), 3, '0') || v_rm_id;
v_string := v_string || '183' || lpad(1, 3, '0') || '0';
endif;
However I cannot get the final string to properly have two decimals in all use cases.
For example if the value in the db is 125 I get this for a final string
144450034629999008009Bob Smith05900100{89006125}1060073307542164001017400340917500
34091830010
however it should have been
144450034629999008009Bob Smith05900100{89007125.00}1060073307542164001017400340917500
34091830010
Sorry for the formatting above, I can't see how to bold a section without a code block so I've highlighted the relative portions instead {}
What am I missing if I need to always display a number to two decimals even if a whole or 1 decimal value is given?
You actually, have to insert the data with formatting. (Assuming the targeted column is VARCHAR) What ever format you fetch and put into a NUMBER variable, will be a NUMBER.
A number doesn't have a format to be saved after-all. Only for display, the formatting comes into picture.
insert into SQL_TXT values (TO_CHAR(v_credit_amt,'FM99999.00'));
commit;
If the INSERT-ed column is NUMBER again.
You still want to go with
SELECT TO_CHAR(COL1,'FM99999.00') FROM SQL_TEXT;