FUNCTION ST_Distance_Sphere does not exist in MariaDB - database

I want get all locations around my location but the function ST_Distance_Sphere does not work.
My query:
select *, astext(location) as location from `locations`
where ST_Distance_Sphere(location, POINT(35.905069591297, 49.765869174153)) < 1000
Error :
SQLSTATE[42000]: Syntax error or access violation:
1305 FUNCTION app.ST_Distance_Sphere does not exist (SQL:
select *, astext(location) as location from `locations`
where ST_Distance_Sphere(location, POINT(35.905069591297, 49.765869174153)) < 1000)

For those who still need the function in MariaDB, you can create the function based on the formula
CREATE FUNCTION `st_distance_sphere`(`pt1` POINT, `pt2` POINT) RETURNS
decimal(10,2)
BEGIN
return 6371000 * 2 * ASIN(SQRT(
POWER(SIN((ST_Y(pt2) - ST_Y(pt1)) * pi()/180 / 2),
2) + COS(ST_Y(pt1) * pi()/180 ) * COS(ST_Y(pt2) *
pi()/180) * POWER(SIN((ST_X(pt2) - ST_X(pt1)) *
pi()/180 / 2), 2) ));
END

10.2+
This issue has been fixed and backported with MDEV-13467. It's available 10.2.38, 10.3.29, 10.4.19, 10.5.10
Find their support matrix here.

http://mysql.rjweb.org/doc.php/find_nearest_in_mysql#gcdistdeg
That blog discusses multiple ways of "finding nearest" on the globe in MySQL/MariaDB. As part of that discussion, I developed that Stored Function.

I think that's it ST_DISTANCE https://mariadb.com/kb/en/library/st_distance/

Related

Sqlite3 calculate the percentages from totals

Question(*):
The total number of cases and deaths as a percentage of the population, for each country (with country, % cases of population, % deaths of population as columns)
I have two tables :
countriesAffected(countriesAndTerritories,geoId,countryterritoryCode,popData2019,continentExp)
victimsCases(dateRep,cases,deaths,geoId)
where primary key(geoid)
I tried to do (*) by this method:
SELECT countriesAndTerritories, (100 *SUM(victimsCases.cases) / popData2019)as "cases" ,(100 * SUM(deaths) / popData2019) as "deaths"
FROM countriesAffected
INNER JOIN victimsCases ON victimsCases.geoId = countriesAffected.geoId
GROUP BY countriesAndTerritories
ORDER BY countriesAndTerritories DESC;
Error: near line 2: near "SELECT countriesAndTerritories": syntax error
But for some reason I get all types of syntax errors, i tried to sort it out but with no results. And not sure where did i went wrong.
If you are getting the error Error: near line 2: near "SELECT countriesAndTerritories": syntax error then the issue is with LINE 1 (perhaps no ; at the end of line 1).
Otherwise your query works albiet probably not as intended (as you may well want decimal places for the percentages).
Consider the following (that shows your SQL with additional SQL added to work as intended (see casesV2 and deathsV2 that utilise CAST to force INTEGER to REAL)).
DROP TABLE If EXISTS victimsCases;
DROP TABLE IF EXISTS countriesAffected;
CREATE TABLE IF NOT EXISTS countriesAffected (countriesAndTerritories TEXT,geoId INTEGER PRIMARY KEY,countryterritoryCode TEXT,popData2019 INTEGER,continentExp TEXT);
CREATE TABLE IF NOT EXISTS victimsCases (dateRep TEXT,cases INTEGER ,deaths INTEGER,geoId INTEGER);
INSERT INTO countriesAffected VALUES
('X',1,'XXX',10000,'?'),('Y',2,'YYY',20000,'?'),('Z',3,'ZZZ',30000,'?')
;
INSERT INTO victimsCases VALUES
('2019-01-01',100,20,1),('2019-01-02',100,25,1),('2019-01-03',100,15,1),
('2019-01-01',30,5,2),('2019-01-02',33,2,2),
('2019-01-01',45,17,3),('2019-01-02',61,4,3),('2019-01-03',75,7,3)
;
SELECT countriesAndTerritories,
(100 *SUM(victimsCases.cases) / popData2019)as "cases", /* ORIGINAL */
(100 * SUM(deaths) / popData2019) as "deaths", /* ORIGINAL */
CAST(SUM(victimsCases.cases) AS FLOAT) / popData2019 * 100 AS "casesV2",
CAST(SUM(victimscases.deaths) AS FLOAT) / popData2019 * 100 as "deathsV2"
FROM countriesAffected
INNER JOIN victimsCases ON victimsCases.geoId = countriesAffected.geoId
GROUP BY countriesAndTerritories
ORDER BY countriesAndTerritories DESC;
DROP TABLE If EXISTS victimsCases;
DROP TABLE IF EXISTS countriesAffected;
The result of the above is :-

Entity Framework updates with wrong values after insert

This issue is discovered because I have an object with a field calculated off the ID, which contains the ID as part of it with a prefix and a checksum digit. It is a requirement that these calculated values are unique, but they also cannot be random, so this seemed the best way to do it.
The code in question looks like this:
entity = new Entity() { /* values */ };
context.SaveChanges(); //generate the ID field
entity.CALCULATED_FIELD = CalculateField(prefix, entity.ID);
This works just fine in 99% of cases, but occasionally we get a value in the database which looks like:
ID: 1234
CALCULATED_FIELD : prefix000{1233}8
EXPECTED: prefix000{1234}3
With the parts in the braces being calculated from the ID column.
The fact that the calculated field is incorrect is bad enough, but the implication is that upon doing a savechanges, there is no guarantee that the row returned to Entity Framework is the one which was originally worked on! I am looking into using a stored procedure on insert in order to fix the generated field problem, but in the long run we're going to have lots of bad data if we keep working on the wrong rows.
When I told entity framework to map the table to stored procedures it generated the following boilerplate code:
INSERT [dbo].[tableName](fields...)
VALUES(values...)
DECLARE #ID int
SELECT #ID = [ID]
FROM [dbo].[tableName]
WHERE ##ROWCOUNT > 0 AND [ID] = scope_identity()
SELECT t0.[ID]
FROM [dbo].[tableName] as t0
WHERE ##ROWCOUNT > 0 AND t0.[ID] = #ID
The best idea I can come up with is that an extra insert could occur before scope_identity() is called. We are migrating this system from using stored procedures where we used ##IDENTITY in place instead, could there be a difference there?
EDIT: CalculateField:
public static string CalculateField(string prefix, int ID)
{
var calculated = prefix.PadRight(17 - ID.ToString().Length)
.Replace(" ", "0") + ID.ToString();
var multiplier = 3;
var sum = 0;
foreach (char c in calculated.ToCharArray().Reverse())
{
sum += multiplier * int.Parse(c.ToString());
multiplier = 4 - multiplier;
}
if (sum % 10 == 0) { return calculated + "0"; }
return calculated + (10 - (sum % 10)).ToString();
}
UPDATE: Changing the called method from static to an instance method and only running it later after additional changed were made instead of straight after creation appears to have solved the problem, for reasons I can't comprehend. I'm leaving the question open for now since I don't yet have a large enough sample to be completely sure the problem is resolved, and also because I have no explanation for what really changed.

Using SQL WHERE LIKE to filter when have consecutive numbers

Trying to convert a PostgreSQL view to SQL Server (2016) view.
I have a table with a column named filename, with the following data:
R24bMP1.png
MP3.png
R28.jpg
I002.jpg
App_1472669569054.jpg
Test_1575753047890.png
So, I like to filter all rows the filename must contains 13 consecutives numbers, in the example, only App_1472669569054.jpg and Test_1575753047890.png.
In PostgreSQL, I can use regex to do this:
SELECT * FROM table WHERE filename ~ '\d{13}'.
Tried in SQL Server with:
SELECT * FROM table WHERE filename LIKE '%[0-9]{13}%', but got no results. The only way that worked is:
SELECT * FROM table WHERE filename LIKE '%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%'
After that, I need to get only the number part of filename, in the example, the returned value must be:
1472669569054
1575753047890
I know I can use CLR with SQL Server, but I like to known if is possible to filter without CLR in this case.
As Martin Smith pointed out:
'%[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]%'
can be reduced to:
'%' + replicate('[0-9]',13) + '%'
You were most of the way there:
declare #filename varchar(128) = 'Test_1575753047890.png'
select test=substring(#filename
,patindex('%' + replicate('[0-9]',13) + '%',#filename)
,13
)
returns: 1575753047890
So for your table it would look like:
select test=substring([filename]
,patindex('%' + replicate('[0-9]',13) + '%',[filename])
,13
)
from t
where patindex('%' + replicate('[0-9]',13) + '%',[filename]) > 0

How to find power of a number in SQLite

I want to update the Interest field in my database. My SQL query is like as per below
Update Table_Name set Interest = Principal * Power(( 1 + (rate /
100),year)
This query works fine in MySQL but don't work with SQLite.
The error says that No Power funcation found
Does anyone know how to resolve this problem as I have to do this using query to update more than 3000 records at a time.
SQLite doesn't have a lot of functions available. But the good news is that is easy enough to add your own.
Here's how to do it using the C API (which also works from Objective-C code).
First write a power function:
void sqlite_power(sqlite3_context *context, int argc, sqlite3_value **argv) {
double num = sqlite3_value_double(argv[0]); // get the first arg to the function
double exp = sqlite3_value_double(argv[1]); // get the second arg
double res = pow(num, exp); // calculate the result
sqlite3_result_double(context, res); // save the result
}
Then you need to register the function:
int res = sqlite3_create_function(dbRef, "POWER", 2, SQLITE_UTF8, NULL, &sqlite_power, NULL, NULL);
The 2 is the number of arguments for the function. dbRef is of course the sqlite3 * database reference.
You can also create an SQLite user-defined function from python. Based on the example at docs.python.org: sqlite3.Connection.create_function
Create a python function:
def sqlite_power(x,n):
return int(x)**n
print(sqlite_power(2,3))
# 8
Create a SQLite user-defined function based on the python function:
con = sqlite3.connect(":memory:")
con.create_function("power", 2, sqlite_power)
Use it:
cur = con.cursor()
cur.execute("select power(?,?)", (2,3))
print cur.fetchone()[0]
# 8
I was strugginling with this too, but if all you need is powers of 2 (or multiples, etc) there is a simpler way:
Use the shift operator, e.g
SELECT 1 << mytable.value
SELECT 1 << (table.x + etc..)
https://www.cafe-encounter.net/p3244/installing-and-using-sqlite-extensions-on-macos-and-maybe-windows-linux-too
Step was to build the Math extensions library that some wonderful person named Liam Healy wrote:
Enter following command in terminal :
Step 1) Download/ Open link http://sqlite.org/contrib/download/extension-functions.c?get=25
Step 2) Go to location where extension-functions.c is downloaded. Run command "gcc -fno-common -dynamiclib extension-functions.c -o libsqlitefunctions.dylib". This will create file libsqlitefunctions.dylib at same place then you can use that in your ios application from xcode.
Now in your cocoa app you can add:
“SELECT load_extension(’libsqlitefunctions.dylib’);”
and then you have access to all kinds of glorious methods like COS, SQRT, etc! You can use them in your app like this:
//Activate database loading
sqlite3_enable_load_extension(database, 1);
sqlite3_load_extension(database,”libsqlitefunctions.dylib”,0,0);
If you are using SQLite NuGet package in a .NET project, you can write an extension method and bind it at runtime;
[SQLiteFunction("pow", 2, FunctionType.Scalar)]
public class SQLitePowerExtension : SQLiteFunction
{
public override object Invoke(object[] args)
{
double num = (double)args[0];
double exp = (double)args[1];
return Math.Pow(num, exp);
}
}
And then use it like this;
using (var conn = new SQLiteConnection("Data Source=:memory:"))
{
conn.Open();
conn.BindFunction(typeof(SQLitePowerExtension).GetCustomAttribute<SQLiteFunctionAttribute>(), new SQLitePowerExtension());
var comm = new SQLiteCommand("CREATE TABLE test (num REAL, exp REAL, result REAL)", conn);
comm.ExecuteNonQuery();
// Populate with some data - not shown
comm = new SQLiteCommand($"UPDATE test SET result = pow(num, exp))", conn);
comm.ExecuteNonQuery();
}
This could be solved with SQL. This works for integer exponents:
Drop Table if Exists args ;
Create Table args as Select 2.5 as Base, 4 as Exponent ;
WITH RECURSIVE pow(exponent, exponent_remainder, base, result) as (
--FIRST EXPRESSION
SELECT exponent,exponent -1 , base,base
FROM args
union all
--SECOND EXPRESSION
select Args.exponent,pow.exponent_remainder -1, pow.base,pow.result * pow.base
from args
join pow on args.exponent = pow.exponent
where pow.exponent_remainder >= 0
)
select pow.result
from pow
where pow.exponent_remainder = 0;
Actually sqlite does have pow/power as a built-in mathematical function, but you need to enable it with DSQLITE_ENABLE_MATH_FUNCTIONS.
From https://www.sqlite.org/lang_mathfunc.html#pow:
The math functions shown below are part of the SQLite amalgamation source file but are only active if the amalgamation is compiled using the -DSQLITE_ENABLE_MATH_FUNCTIONS compile-time option.
SQLite doesn't provide a power function or operator. You'll have to implement it yourself via sqlite3_create_function….

Error using to_char // to_timestamp

I have a database in PostgreSQL and I'm developing an application in PHP using this database.
The problem is that when I execute the following query I get a nice result in phpPgAdmin but in my PHP application I get an error.
The query:
SELECT t.t_name, t.t_firstname
FROM teachers AS t
WHERE t.id_teacher IN (
SELECT id_teacher FROM teacher_course AS tcourse
JOIN course_timetable AS coursetime
ON tcourse.course = coursetime.course
AND to_char(to_timestamp('2010-4-12', 'YYYY-MM-DD'),'FMD') = (coursetime.day +1)
)
AND t.id_teacher NOT IN (
SELECT id_teacher FROM teachers_fill WHERE date = '2010-4-12'
)
ORDER BY t.t_name ASC
And this is the error in PHP
operator does not exist: text = integer (to_timestamp('', 'YYYY-MM-DD'),'FMD') =
(courset... ^ HINT: No operator matches the given name and argument type(s).
You might need to add explicit type casts.
The purpose to solve this error is to use the ORIGINAL query in php with :
$date = "2010"."-".$selected_month."-".$selected_day;
SELECT ...
AND to_char(to_timestamp('$date', 'YYYY-MM-DD'),'FMD') = (coursetime.day +1)
)
AND t.id_teacher NOT IN (
SELECT id_teacher FROM teachers_fill WHERE date = '$date'
)
The error message seems quite clear to me. You are mixing strings and numbers. More precisely, you are converting a string ('2010-4-12') to a timestamp, then to a string, then comparing to an int. This is a type mess, and postgresql is quite strict with typing (for good reasons). What are you trying to do here ?
to_char(to_timestamp('2010-4-12', 'YYYY-MM-DD'),'FMD') = (coursetime.day +1))
Further, you should use a TIMESTAMP, just a DATE.
If (I'm not sure) you are tring to compare the day of week from a date formated as 'YYYY-MM-DD' to a given value (as an integer), you should better use date_part. For example (not tested):
date_part('dow' , to_date('2010-4-12', 'YYYY-MM-DD') ) = coursetime.day + 1

Resources