SQL - The used select statement have a different number of colums - mysql

I'm trying to make my first function, it creates without any error, but, when I try to use it it gives me error.
Here's the function -
CREATE FUNCTION isie_kontakti (condition CHAR(3))
RETURNS CHAR(100)
BEGIN
DECLARE returnthis CHAR(100);
SELECT DISTINCT Person.name, Person.lastName, Contacts.mobile, Contacts.email
FROM Person JOIN Contacts on Contacts.Person_ID = Person.ID
JOIN ParentChild on ParentChild.parentID = Person.ID
JOIN ChildGroup ON ChildGroup.Person_ID = ParentChild.childID
WHERE ChildGroup.Group_ID = 'condition' INTO returnthis;
RETURN returnthis;
END//
Table schema - http://www.imagesup.net/dm-713886347846.png

You create your function to return a single column of type char(100) yet the returnthis item contains quite a few columns.
You need to match up your query and return type.
How you do that depends on what you're trying to achieve. It's possibly as simple as just concatenating the columns from the select into a single variable, something along the lines of (untested since I don't have my DBMS available at the moment):
SELECT Person.name | ' '
| Person.lastName | ' '
| Contacts.mobile | ' '
| Contacts.email
FROM ...

Related

How do i batch multiple select query calls into a single mysql query or procedure

I have this function which checks friendship status between 2 users using their userIds and it is working fine.
function checkFriendShipBetweenUsers(user1Id, user2Id) {
var checkFriendShipBetweenUsersQuery = "SELECT status FROM friends WHERE (user1Id=? AND user2Id =?) OR (user1Id=? AND user2Id =?)"
var queryParameterList = [user1Id, user2Id, user2Id, user1Id]
}
I have a case in which i need to check friendship status between a user and other 3 users.
I can call above function 3 times, one for each other user to get desired result but i would like to make it with a single db call using a single query or using a mysql procedure.
function checkFriendShipBetweenUsers(user1Id, userIdList) {
var checkFriendShipBetweenUsersQuery = ""
var queryParameterList = []
}
So this query/procedure call should return 3 integers indicating user1's friendship status with users in userIdList.
Here is an example db fiddle:
db-fiddle.com/f/p5RP61V3AcawRgJcogeXey/1
given user1Id : 'a8t57h6p8n2efden' and
userIdList : ['typ3vg6xb1vt7nw2', 'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl', '0bw87kprb97pes1crom8ceodi07r2kd0']
How do i write such query or procedure?
DEMO
-- source data
CREATE TABLE test (
id INT,
user1Id VARCHAR(100),
user2Id VARCHAR(100),
status INT
);
INSERT INTO test (id,user1Id,user2Id,status) VALUES
(1,'a8t57h6p8n2efden','typ3vg6xb1vt7nw2',0),
(2,'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl','a8t57h6p8n2efden',1),
(3,'0bw87kprb97pes1crom8ceodi07r2kd0','a8t57h6p8n2efden',2),
(4,'a8t57h6p8n2efden','ap21wzbew0bprt5t',0);
SELECT * FROM test;
id
user1Id
user2Id
status
1
a8t57h6p8n2efden
typ3vg6xb1vt7nw2
0
2
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
a8t57h6p8n2efden
1
3
0bw87kprb97pes1crom8ceodi07r2kd0
a8t57h6p8n2efden
2
4
a8t57h6p8n2efden
ap21wzbew0bprt5t
0
-- searching parameters
SET #user1Id := 'a8t57h6p8n2efden';
SET #userIdList := '[
"typ3vg6xb1vt7nw2",
"cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl",
"0bw87kprb97pes1crom8ceodi07r2kd0",
"absent value"
]';
SELECT jsontable.userid, test.status
FROM JSON_TABLE( #userIdList,
'$[*]' COLUMNS ( rowid FOR ORDINALITY,
userid VARCHAR(255) PATH '$'
)) jsontable
LEFT JOIN test
ON (#user1Id, jsontable.userid) IN ( (test.user1Id, test.user2Id),
(test.user2Id, test.user1Id)
)
userid
status
typ3vg6xb1vt7nw2
0
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
1
0bw87kprb97pes1crom8ceodi07r2kd0
2
absent value
null
fiddle
If you do not need status value for the IDs which are not found then use INNER JOIN.
If you want to receive the output as one solid value then add according GROUP BY and aggregation. Use jsontable.rowid for to provide needed values ordering.
PS. If you won't use an aggregation then you may do not obtain rowid value - simply remove rowid FOR ORDINALITY, in this case.

How can I iterate a JSON in SQL?

So I have a JSON variable with several values like this:
"["1", "2", "3", "4"]"
What i need to do is pass that value to an SQL procedure to mount a query in which the WHERE clause adds all the values in the JSON, so something like parsing the JSON, interate it and concat it in order to get an #where similar to:
AND id=1 AND id=2 AND id=3 AND id=4
I tried something like this, as something really similar is taking place in an already existing procedure, but doesn't work:
SET #idWhere="";
IF id IS NOT NULL AND JSON_EXTRACT(id, '$[0]') IS NOT NULL THEN
SET #idWhere = CONCAT(#idWhere," AND JSON_SEARCH('",id,"','one',id) IS NOT NULL ");
END IF;
Where id is both the name of the JSON and the column name.
Thanks in advance!
If you are running MySQL 8.0, you can use json_table() to turn the array to a recordset, and then aggregate the results with group_concat():
select group_concat(concat('id = ', i) separator ' and ') id_where
from json_table(
'["1", "2", "3", "4"]',
"$[*]" columns(i int path '$')
) as t
In this demo on DB Fiddle, this yields:
| id_where |
| --------------------------------------- |
| id = 1 and id = 2 and id = 3 and id = 4 |
NB: you probably want ors, not ands.

How detect the two word of a string like “helpme”?

I have a dictionary table (words) and another table with concatenated 2 words like "helpme", "helloword" "loveme"...
I want to transform this table to "help me", "hello word", "love me"
I run this sequence :
SELECT
table_concatened.twowords,
t1.word as 'word1',
t2.word as 'word2'
FROM
table_concatened
JOIN dictionary_table AS t1 ON SUBSTRING(table_concatened.twowords,1,len(t1.word)) = t1.word
JOIN dictionary_table AS t2 ON SUBSTRING(table_concatened.twowords,len(t1.word)+1,len(table_concatened.twowords)) = t2.word;
It is working, but is took a very long time with my table.
How can I optimise my sql sequence?
---- exemple of table ---
dictionary_table
|hello|
|word |
|love |
|me |
exemple of table_concatened :
|helloword|
|loveyou |
Edit:
1) The use case is for autocorrection. For example on skype, on iPhone, on chrome, when I type "helloword", I have auto correction to "hello word".
2) The database here is not very important. Our issue is about algo logic and performance optimisation.
If you don't mind going dynamic (and if SQL Server)
-- Generate Some Sample Data
Declare #Dictionary_Table table (word varchar(50));Insert Into #Dictionary_Table values ('hello'),('word'),('love'),('me')
Declare #table_concatened table (ID int,twowords varchar(50));Insert Into #table_concatened values (1,'helloword'),(2,'loveyou')
-- Generate SQL and Execute
Declare #SQL varchar(max)=''
Select #SQL = #SQL+concat(',(',ID,',''||',replace(twowords,'''',''''''),'||'')') From #table_concatened --Where ID=2
Select #SQL = Replace(#SQL,MapFrom,MapTo)
From (
Select MapFrom = word
,MapTo = '|'+ltrim(rtrim(word))+'|'
From #Dictionary_Table
Union All
Select '|',' ' -- Remove Any Remaining |
Union All
Select ' ',' ' -- Remove Any Remaining |
) A
Select #SQL = 'Select ID,Value=ltrim(rtrim(Value)) From ('+Stuff(#SQL,1,1,'values')+') N(ID,Value)'
Exec(#SQL)
Returns
ID Value
1 hello word
2 love you

How to find if a function exists in PostgreSQL?

Unlike tables or sequences, user-defined functions cannot be found through pg_class. There are questions on how find a list of all functions to delete or grant them, but how to find an individual function (with known name and argument types) is not self-evident from them. So how to find whether a function exists or not?
EDIT: I want to use it in a function, in automated manner. Which solution is the best performance-wise? Trapping errors is quite expensive, so I guess the best solution for me would be something without the extra step of translating error to false, but I might be wrong in this assumption.
Yes, you cannot to find functions in pg_class because functions are stored on system table pg_proc
postgres-# \df
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------------------+------------------+----------------------+--------
public | foo | integer | a integer, b integer | normal
public | function_arguments | text | oid | normal
(2 rows)
Query for list of custom functions based on pg_proc is simply
postgres=# select p.oid::regprocedure
from pg_proc p
join pg_namespace n
on p.pronamespace = n.oid
where n.nspname not in ('pg_catalog', 'information_schema');
oid
-------------------------
foo(integer,integer)
function_arguments(oid)
(2 rows)
Most simply and fastest tests on functions existence are casting (without parameters) to regproc or regprocedure (with parameters):
postgres=# select 'foo'::regproc;
regproc
---------
foo
(1 row)
postgres=# select 'foox'::regproc;
ERROR: function "foox" does not exist
LINE 1: select 'foox'::regproc;
^
postgres=# select 'foo(int, int)'::regprocedure;
regprocedure
----------------------
foo(integer,integer)
(1 row)
postgres=# select 'foo(int, text)'::regprocedure;
ERROR: function "foo(int, text)" does not exist
LINE 1: select 'foo(int, text)'::regprocedure;
^
or you can do some similar with test against pg_proc
postgres=# select exists(select * from pg_proc where proname = 'foo');
exists
--------
t
(1 row)
postgres=# select exists(select *
from pg_proc
where proname = 'foo'
and function_arguments(oid) = 'integer, integer');
exists
--------
t
(1 row)
where:
CREATE OR REPLACE FUNCTION public.function_arguments(oid)
RETURNS text LANGUAGE sql AS $function$
select string_agg(par, ', ')
from (select format_type(unnest(proargtypes), null) par
from pg_proc where oid = $1) x
$function$
or you can use buildin functions:pg_get_function_arguments
p.s. trick for simply orientation in system catalog. Use a psql option -E:
[pavel#localhost ~]$ psql -E postgres
psql (9.2.8, server 9.5devel)
Type "help" for help.
postgres=# \df
********* QUERY **********
SELECT n.nspname as "Schema",
p.proname as "Name",
pg_catalog.pg_get_function_result(p.oid) as "Result data type",
pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
CASE
WHEN p.proisagg THEN 'agg'
WHEN p.proiswindow THEN 'window'
WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'
ELSE 'normal'
END as "Type"
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE pg_catalog.pg_function_is_visible(p.oid)
AND n.nspname <> 'pg_catalog'
AND n.nspname <> 'information_schema'
ORDER BY 1, 2, 4;
**************************
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+--------------------+------------------+----------------------+--------
public | foo | integer | a integer, b integer | normal
public | function_arguments | text | oid | normal
(2 rows)
I think the easiest way would be to use pg_get_functiondef().
If it returns something, the function is there, otherwise the function does not exist:
select pg_get_functiondef('some_function()'::regprocedure);
select pg_get_functiondef('some_function(integer)'::regprocedure);
The drawback is that it will produce an error if the function isn't there instead of simply returning an empty result. But this could e.g. be overcome by writing a PL/pgSQL function that catches the exception and returns false instead.
Based on #PavelStehule answer this is how I am checking this in my scripts (using postgres exceptions and available exception codes)
DO $_$
BEGIN
BEGIN
SELECT 'some_schema.some_function(text)'::regprocedure;
EXCEPTION WHEN undefined_function THEN
-- do something here, i.e. create function
END;
END $_$;
Late to the party,
but it could be something like this (don't use select instead of perform if you are not using the result or you would get an error complaining about it :
ERROR: query has no destination for result data
So the following code will work :
DO $$
BEGIN
BEGIN
perform pg_get_functiondef('some_function()'::regprocedure);
raise notice 'it exists!';
EXCEPTION WHEN undefined_function THEN
raise notice 'Does not exist';
END;
END $$;

Conditional Join: don't join on null

I have relationships that might not necessarily exist (they could be optional i.e. null); for example, a image may not have an address so it may be null.
I am unsure how to not return all null values.
Is there some condition I can put in place on the join that says if the address is null don't do a join and don't return all the null columns?
SELECT im.title, im.alias_title, im.description, im.main_image, im.hits,
im.show_comment, im.created_on, im.date_taken, im.account_type_id,
c.make, c.model, ad.address_line_1, ad.address_line_2,
spc.state_province_county, tvc.town_village_city, co.country,
ge.latitude, ge.longitude, ge.zoom, ge.yaw, ge.pitch,
us.first_name, us.surname, us.user_set_online, ut.username,
ut.account_type_id, aty.`type`, ufy.realname, ufy.location,
ufy.location, ufy.account_type_id
FROM image im
INNER JOIN user us
ON im.user_id = us.id
LEFT JOIN user_type ut
ON us.id = ut.user_id
LEFT JOIN user_flickr_youtube ufy
ON ut.id = ufy.user_type_id
LEFT JOIN account_type aty
ON ut.account_type_id =aty.id
LEFT JOIN address ad
ON im.address_id = ad.id
LEFT JOIN state_province_county spc
ON ad.state_province_county_id = spc.id
LEFT JOIN town_village_city tvc
ON ad.town_village_city_id =tvc.id
LEFT JOIN country co
ON ad.country_id =co.id
LEFT JOIN geolocation ge
ON im.geolocation_id = ge.id
LEFT JOIN camera c
ON im.camera_id = c.id
WHERE im.alias_title = 'test'
AND im.approved = 'Yes'
AND im.visible = '1'
LIMIT 1;
Is there some condition i can put in place on the join that says if the address is null dont do a join and dont bring me back all the null columns
Yes; you can run a JOIN instead of a LEFT JOIN. But that won't simply exclude the address if it is NULL, it will ignore the whole row altogether.
Usually this kind of situation is either handled by supplying a default value, possibly empty, for example directly in MySQL
SELECT
...COALESCE(ad.address_line_1,'(no address)') AS address_line_1,
COALESCE(ad.address_line_2,'') AS address_line_2, ...
or it is handled by the application:
if row['address_line_1']:
result = result + ("<td class=\"address\">%s</td>" % ( row['address_line_1'] ))
...
This also because a query could potentially return not one record, but several, and of these, some might have a NULL colum and some might not.
UPDATE
There is a way, but it's likely to make milk go sour in cows fifty miles downrange.
This is a proof of concept, on a MUCH smaller query and table, and takes advantage of the possibility of dynamically building a query.
First of all we have our query WHERE condition, here represented by "id = 1". We want to have the name column if the name column is not NULL.
SELECT #address := COALESCE(MIN(',name'),'') FROM client WHERE name IS NOT NULL AND id = 1;
This will return an empty string if the selected column is NULL. Otherwise it will return a comma and the name of that column.
This is the statement that in your case will be humongous, given your query. It contains the same WHERE as before, without the request that the name be NULL. And the field list is now dynamic.
SELECT #string := CONCAT('SELECT id', #address, ' FROM client WHERE id = 1');
Except that #string is, well, a string. To execute it as a query we do
PREPARE query FROM #string;
EXECUTE query;
DEALLOCATE PREPARE query;
How this might interact with your application, I do not dare fathom. I have tried an implementation in PHP on an expendable VM :-), cycling between the values of 1 and 3 (one row has a NULL name, one hasn't).
<?php
// Connect to this VM's local DB
mysql_connect('localhost','root','') or die("Cannot connect");
mysql_select_db('test');
foreach(array(1, 3) as $id)
{
mysql_query("SELECT #address := COALESCE(MIN(',name'),'') FROM client WHERE name IS NOT NULL AND id = $id;");
mysql_query("SELECT #string := CONCAT('SELECT id', #address, ' FROM client WHERE id = ', $id);");
mysql_query("PREPARE query FROM #string;");
$exec = mysql_query("EXECUTE query;");
while($tuple = mysql_fetch_assoc($exec))
{
print implode(" | ", $tuple) . "\n";
}
mysql_query("DEALLOCATE PREPARE query;");
}
?>
The answer seems to indicate it's working:
1 | Rossi
3
(I wouldn't have been surprised if it returned something like 'Ia! Cthulhu fhtagn!').