MySQL check table exists with IF function - mysql

SELECT IF((
SELECT COUNT(TABLE_NAME)
FROM information_schema.tables
WHERE TABLE_SCHEMA='database0' AND TABLE_NAME='table'
) >0 , (
SELECT id
FROM database1.table
WHERE id NOT IN (SELECT id FROM database0.table)
), NULL) AS pk_value;
The table database0.table may not exist; if the table does not exist, I want to skip the true clause of the IF statement. Even when the IF statement should return NULL, I get the error that database0.table does not exist. What can I do so that the query returns NULL instead of throwing an error if the table does not exist?

As mentioned in comments, queries are error-checked before they are run, so referring to a non-existent table won't work. You can store it as a function, which is only evaluated at run-time:
DROP FUNCTION IF EXISTS get_pk;
DELIMITER //
CREATE FUNCTION get_pk()
RETURNS INT
BEGIN
DECLARE counter INT;
DECLARE pk_value INT;
SELECT COUNT(table_name) INTO counter
FROM information_schema.tables
WHERE table_schema = 'database0' AND table_name = 'table';
IF counter = 1 THEN
SELECT id INTO pk_value
FROM database1.table
WHERE id NOT IN (SELECT id FROM database0.table);
ELSE
SELECT NULL INTO pk_value;
END IF;
RETURN pk_value;
END//
DELIMITER ;
Then access the value like this:
SELECT get_pk();

Related

why mysql procedure return null

I have a simple mysql procedure which must return an query string. But, it returns almost every time QueryResult (column name) as <null> value.
create procedure return_table_rename_query(
IN targetTable VARCHAR(100),
IN tblPrefix VARCHAR(100)
)
BEGIN
SET #returnQuery = CONCAT('SELECT "MYSQLIMPORT can not rename table for target ', #targetTable, '";');
SET #totalRows = (SELECT COUNT(*) FROM table);
if IFNULL(#totalRows, 0) > 0
then
SET #returnQuery = CONCAT('drop table if exists table_name.', ...);
end if;
SELECT #returnQuery AS 'QueryResult';
end;
#targettable is not the same variable as targettable - you are mixing user defined variables and parameter variables and it seems likely that #targettable is null and if any element in a concat is null then the result is null.
Please read How to declare a variable in MySQL?

MySQL Use table name for function

When we use a statement like select count(*) from TABLE, the function count() automatically knows which table it is counting. Is it possible to grab the table and use it in a user defined function.
drop function if exists related_count;
create function related_count(parent int(11)) returns int(11) deterministic
begin
declare count int(11) default 0;
set count=(select count(*) from TABLENAME where id=parent);
return count;
end;
So that I can use it like this:
select count(*),related_count(id) from TABLENAME
So that I can use the same function regardless of table instead of defining multiple functions because of multiple tables.
Is there a way to switch between select count(*) from TABLENAME1 where id=parent or select count(*) from TABLENAME2 where id=parent dependent on a variable related_count('TABLE1',id)
The comment above from #RajeevRanjan mentions using dynamic SQL. This won't work, but if it did it would look like this:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default 0;
set #sql = concat('select count(*) into count from `', tablename, '` where id = ', parent);
prepare stmt from #sql;
execute stmt;
return count;
end
However, this is not allowed:
ERROR 1336 (0A000): Dynamic SQL is not allowed in stored function or trigger
The reason it doesn't work is that your stored function could be called by an expression in an SQL statement that is itself a dynamic SQL execution. I guess MySQL only allows one level "deep" of prepare/execute. You can't make a prepared query run another prepared query.
To do this, you'd have to hard-code each table name like the following:
create function related_count(tablename varchar(64), parent int) returns int reads sql data
begin
declare count int default null;
if tablename = 'foo' then set count = (select count(*) from foo where id = parent);
elseif tablename = 'bar' then set count = (select count(*) from bar where id = parent);
elseif tablename = 'baz' then set count = (select count(*) from baz where id = parent);
end if;
return count;
end
This also has an advantage that it isn't an SQL injection vulnerability, whereas the PREPARE/EXECUTE solution (if it had worked) would be.
PS: A function that reads from other tables is not deterministic.

How to get a specific column by its ordinal position in table using SQL? [duplicate]

Is there any SQL lingo to return JUST the first two columns of a table WITHOUT knowing the field names?
Something like
SELECT Column(1), Column(2) FROM Table_Name
Or do I have to go the long way around and find out the column names first? How would I do that?
You have to get the column names first. Most platforms support this:
select column_name,ordinal_position
from information_schema.columns
where table_schema = ...
and table_name = ...
and ordinal_position <= 2
There it´s
declare #select varchar(max)
set #select = 'select '
select #select=#select+COLUMN_NAME+','
from information_schema.columns
where table_name = 'TABLE' and ordinal_position <= 2
set #select=LEFT(#select,LEN(#select)-1)+' from TABLE'
exec(#select)
A dynamic query using for xml path will also do the job:
declare #sql varchar(max)
set #sql = (SELECT top 2 COLUMN_NAME + ',' from information_schema.columns where table_name = 'YOUR_TABLE_NAME_HERE' order by ordinal_position for xml path(''))
set #sql = (SELECT replace(#sql +' ',', ',''))
exec('SELECT ' + #sql + ' from YOUR_TABLE_NAME_HERE')
I wrote a stored procedure a while back to do this exact job. Even though in relational theory there is no technical column order SSMS is not completely relational. The system stores the order in which the columns were inserted and assigns an ID to them. This order is followed using the typical SELECT * statement which is why your SELECT statements appear to return the same order each time. In practice its never a good idea to SELECT * with anything as it doesn't lock the result order in terms of columns or rows. That said I think people get so stuck on 'you shouldn't do this' that they don't write scripts that actually can do it. Fact is there is predictable system behavior so why not use it if the task isn't super important.
This SPROC of course has caveats and is written in T-SQL but if your looking to just return all of the values with the same behavior of SELECT * then this should do the job pretty easy for you. Put in your table name, the amount of columns, and hit F5. It returns them in order from left to right the same as you'd be expecting. I limited it to only 5 columns but you can edit the logic if you need any more. Takes both temp and permanent tables.
EXEC OnlySomeColumns 'MyTable', 3
/*------------------------------------------------------------------------------------------------------------------
Document Title: The Unknown SELECT SPROC.sql
Created By: CR
Date: 4.28.2013
Purpose: Returns all results from temp or permanent table when not knowing the column names
SPROC Input Example: EXEC OnlySomeColumns 'MyTable', 3
--------------------------------------------------------------------------------------------------------------------*/
IF OBJECT_ID ('OnlySomeColumns', 'P') IS NOT NULL
DROP PROCEDURE OnlySomeColumns;
GO
CREATE PROCEDURE OnlySomeColumns
#TableName VARCHAR (1000),
#TotalColumns INT
AS
DECLARE #Column1 VARCHAR (1000),
#Column2 VARCHAR (1000),
#Column3 VARCHAR (1000),
#Column4 VARCHAR (1000),
#Column5 VARCHAR (1000),
#SQL VARCHAR (1000),
#TempTable VARCHAR (1000),
#PermanentTable VARCHAR (1000),
#ColumnNamesAll VARCHAR (1000)
--First determine if this is a temp table or permanent table
IF #TableName LIKE '%#%' BEGIN SET #TempTable = #TableName END --If a temporary table
IF #TableName NOT LIKE '%#%' BEGIN SET #PermanentTable = #TableName END --If a permanent column name
SET NOCOUNT ON
--Start with a few simple error checks
IF ( #TempTable = 'NULL' AND #PermanentTable = 'NULL' )
BEGIN
RAISERROR ( 'ERROR: Please select a TempTable or Permanent Table.',16,1 )
END
IF ( #TempTable <> 'NULL' AND #PermanentTable <> 'NULL' )
BEGIN
RAISERROR ( 'ERROR: Only one table can be selected at a time. Please adjust your table selection.',16,1 )
END
IF ( #TotalColumns IS NULL )
BEGIN
RAISERROR ( 'ERROR: Please select a value for #TotalColumns.',16,1 )
END
--Temp table to gather the names of the columns
IF Object_id('tempdb..#TempName') IS NOT NULL DROP TABLE #TempName
CREATE TABLE #TempName ( ID INT, Name VARCHAR (1000) )
--Select the column order from a temp table
IF #TempTable <> 'NULL'
BEGIN
--Verify the temp table exists
IF NOT EXISTS ( SELECT 1
FROM tempdb.sys.columns
WHERE object_id = object_id ('tempdb..' + #TempTable +'') )
BEGIN
RAISERROR ( 'ERROR: Your TempTable does not exist - Please select a valid TempTable.',16,1 )
RETURN
END
SET #SQL = 'INSERT INTO #TempName
SELECT column_id AS ID, Name
FROM tempdb.sys.columns
WHERE object_id = object_id (''tempdb..' + #TempTable +''')
ORDER BY column_id'
EXEC (#SQL)
END
--From a permanent table
IF #PermanentTable <> 'NULL'
BEGIN
--Verify the temp table exists
IF NOT EXISTS ( SELECT 1
FROM syscolumns
WHERE id = ( SELECT id
FROM sysobjects
WHERE Name = '' + #PermanentTable + '' ) )
BEGIN
RAISERROR ( 'ERROR: Your Table does not exist - Please select a valid Table.',16,1 )
RETURN
END
SET #SQL = 'INSERT INTO #TempName
SELECT colorder AS ID, Name
FROM syscolumns
WHERE id = ( SELECT id
FROM sysobjects
WHERE Name = ''' + #PermanentTable + ''' )
ORDER BY colorder'
EXEC (#SQL)
END
--Set the names of the columns
IF #TotalColumns >= 1 BEGIN SET #Column1 = (SELECT Name FROM #TempName WHERE ID = 1) END
IF #TotalColumns >= 2 BEGIN SET #Column2 = (SELECT Name FROM #TempName WHERE ID = 2) END
IF #TotalColumns >= 3 BEGIN SET #Column3 = (SELECT Name FROM #TempName WHERE ID = 3) END
IF #TotalColumns >= 4 BEGIN SET #Column4 = (SELECT Name FROM #TempName WHERE ID = 4) END
IF #TotalColumns >= 5 BEGIN SET #Column5 = (SELECT Name FROM #TempName WHERE ID = 5) END
--Create a select list of only the column names you want
IF Object_id('tempdb..#FinalNames') IS NOT NULL DROP TABLE #FinalNames
CREATE TABLE #FinalNames ( ID INT, Name VARCHAR (1000) )
INSERT #FinalNames
SELECT '1' AS ID, #Column1 AS Name UNION ALL
SELECT '2' AS ID, #Column2 AS Name UNION ALL
SELECT '3' AS ID, #Column3 AS Name UNION ALL
SELECT '4' AS ID, #Column4 AS Name UNION ALL
SELECT '5' AS ID, #Column5 AS Name
--Comma Delimite the names to insert into a select statement. Bracket the names in case there are spaces
SELECT #ColumnNamesAll = COALESCE(#ColumnNamesAll + '], [' ,'[') + Name
FROM #FinalNames
WHERE Name IS NOT NULL
ORDER BY ID
--Add an extra bracket at the end to complete the string
SELECT #ColumnNamesAll = #ColumnNamesAll + ']'
--Tell the user if they selected to many columns
IF ( #TotalColumns > 5 AND EXISTS (SELECT 1 FROM #FinalNames WHERE Name IS NOT NULL) )
BEGIN
SELECT 'This script has been designed for up to 5 columns' AS ERROR
UNION ALL
SELECT 'Only the first 5 columns have been selected' AS ERROR
END
IF Object_id('tempdb..#FinalNames') IS NOT NULL DROP TABLE ##OutputTable
--Select results using only the Columns you wanted
IF #TempTable <> 'NULL'
BEGIN
SET #SQL = 'SELECT ' + #ColumnNamesAll + '
INTO ##OutputTable
FROM ' + #TempTable + '
ORDER BY 1'
EXEC (#SQL)
END
IF #PermanentTable <> 'NULL'
BEGIN
SET #SQL = 'SELECT ' + #ColumnNamesAll + '
INTO ##OutputTable
FROM ' + #PermanentTable + '
ORDER BY 1'
EXEC (#SQL)
END
SELECT *
FROM ##OutputTable
SET NOCOUNT OFF
SQL doesn't understand the order of columns. You need to know the column names to get them.
You can look into querying the information_schema to get the column names. For example:
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tbl_name'
ORDER BY ordinal_position
LIMIT 2;
You can query the sysobject of the table to find out the first two column then dynamically generate the SQL statement you need.
If you want a permant object that you can query over and over again make a view for each table that only returns the first 2 columns. You can name the columns Column1 and Column2 or use the existing names.
If you want to return the first two columns from any table without any preprocessing steps create a stored procedure that queries the system information and executes a dynamic query that return the first two columns from the table.
Or do I have to go the long way around and find out the column names first? How would I do that?
It's pretty easy to do manually.
Just run this first
select * from tbl where 1=0
This statement works on all major DBMS without needing any system catalogs.
That gives you all the column names, then all you need to do is type the first two
select colname1, colnum2 from tbl

MySQL Replace non-existent records with Not Found while using WHERE IN

How do I replace non-existent records with not found in the following query. Right now I'm just getting the records that are found.
SELECT CASE WHEN `Name` IS NOT NULL THEN `Name` ELSE 'Not Found' END AS Name
FROM `studentDetails`
WHERE `Transaction_ID` IN('496018490c1d5d60beb5', '77888f084c8a0e7578f5')
My input is
'496018490c1d5d60beb5',
'77888f084c8a0e7578f5'
What I'm getting is
Vinod Tonde
My desired output is
Not Found
Vinod Tonde
The Database looks like
I think what you want is this:
SELECT Transaction_ID, CASE WHEN count(*)>0 THEN `Name` ELSE 'Not Found' END AS Name
FROM `studentDetails`
WHERE `Transaction_ID` IN('496018490c1d5d60beb5', 'b6836a07a3c49af6187f')
group by Transaction_ID
OR, using this you could also try:
SELECT IFNULL(Name, 'Not Found') AS Name
FROM (SELECT '496018490c1d5d60beb5' as col
union all
SELECT 'b6836a07a3c49af6187f') temp_table
LEFT JOIN studentDetails
ON temp_table.col=studentDetails.Transaction_ID collate utf8mb4_unicode_ci
Try this:
UPDATE `studentDetails` SET `Name` = 'Not Found' WHERE (`Name` IS NULL OR `Name` = '') AND `Transaction_ID` IN('496018490c1d5d60beb5', 'b6836a07a3c49af6187f')
You can add a few more alternatives to your case
SELECT CASE
WHEN Name IS NULL THEN 'Not Found'
WHEN length(trim(Name)) = 0 THEN 'Not Found'
ELSE Name
END AS Name
FROM studentDetails
WHERE Transaction_ID IN('496018490c1d5d60beb5', 'b6836a07a3c49af6187f')
Edit
Since it seems that one of those two transaction_id doesn't exist in the database at all, you probably need something like this instead
select coalesce(t2.Name, 'Not Found') as Name
from (select '77888f084c8a0e7578f5' as trans union all select '496018490c1d5d60beb5') t1
left join
studentDetails t2
on t1.trans = t2.Transaction_ID
Create a table for the ids to search:
CREATE TABLE ids (id VARCHAR(30));
Create a procedure to split a list of ids separated by a ',' and insert them into the ids table:
CREATE PROCEDURE split_id_list(IN input VARCHAR(300))
BEGIN
DECLARE tot_length int;
DECLARE sub_length int;
my_loop: LOOP
SET tot_length = CHAR_LENGTH(input);
INSERT INTO ids (id) VALUES(SUBSTRING_INDEX(input, ',', 1));
SET sub_length = CHAR_LENGTH(SUBSTRING_INDEX(input, ',', 1))+2;
SET input = MID(input, sub_length, tot_length);
IF input = '' THEN
LEAVE my_loop;
END IF;
END LOOP my_loop;
END;
Create a procedure to generate view containing the results:
CREATE PROCEDURE idsNames(IN id_list VARCHAR(500))
BEGIN
DECLARE a INT;
DECLARE cur1 CURSOR for select count(*) FROM ids;
OPEN cur1;
FETCH cur1 into a;
IF a > 0
THEN DELETE FROM ids;
END IF;
CLOSE cur1;
call split_id_list(id_list);
CREATE OR REPLACE VIEW RESULTS(r_id,r_name) AS
(SELECT ids.id, CASE WHEN Name IS NULL then 'NotFound' ELSE Name END
FROM studentDetails
RIGHT JOIN ids
ON studentDetails.Transaction_ID = ids.id);
END;
Once the table and the procedures are created, each time you want to execute them, just execute the procedure with the required ids and a select from the view:
CALL idsNames('496018490c1d5d60beb5,b6836a07a3c49af6187f');
SELECT r_name FROM RESULTS;

List of table names ordered by their foreign key dependency in MySQL

Given a MySQL database, how do I obtain a list of table names ordered by their foreign key dependency so that I can delete each table? I understand that the foreign key's are stored in the KEY_COLUMN_USAGE table but I'm not sure if there is a utility method to obtain the table names in the correct order. I would prefer a 100% SQL solution.
If your foreign keys follow a naming convention (as they should :), then you could do something simple like
select table_name, group_concat(distinct constraint_name order by constraint_name) as fk_list
from information_schema.key_column_usage
where constraint_name rlike 'fk'
group by table_name
order by fk_list, table_name;
Or, if your foreign keys don't follow a naming convention, then
select table_name, group_concat(distinct constraint_name order by constraint_name) as fk_list
from information_schema.key_column_usage
where referenced_table_name is not null
group by table_name
order by fk_list, table_name;
Try this:
CREATE DEFINER=`root`#`localhost` PROCEDURE `getDependencyOrder`()
BEGIN
DECLARE done INT DEFAULT 0;
declare _id int;
declare _name varchar(50);
declare _refname varchar(50);
declare cur cursor for
select table_name, referenced_table_name
from information_schema.key_column_usage
where constraint_schema = 'your_schema_name';/*************/
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
drop table if exists temp;
create temporary table temp (id int, tablename varchar(50)) engine=memory;
open cur;
set _id = 1000000;
REPEAT
FETCH cur INTO _name, _refname;
IF NOT done THEN
if not exists(select * from temp where tablename = _name) then
insert into temp(id, tablename) values(_id, _name);
set _id = _id + 1000;
end if;
end if;
UNTIL done END REPEAT;
CLOSE cur;
set done = 0;
open cur;
REPEAT
FETCH cur INTO _name, _refname;
IF NOT done THEN
if not (_refname is null) then
if _name <> _refname then
#Buscar el id de la tabla
select id into #Id from temp where tablename = _name;
#Buscar el id de la tabla de referencia, si su id es mayor, ajustar su id
select id into #refId from temp where tablename = _refname;
if #refId > #Id then
select max(id) + 1000 into _id from temp where id < #Id;
if _id is null then
select min(id) - 1000 into _id from temp;
end if;
if exists(select * from temp where id = _id) then
select max(id) into #max from temp where id < #Id;
set _id = (#max + #Id) / 2;
end if;
update temp set id = _id where id = #refId;
end if;
end if;
end if;
end if;
UNTIL done END REPEAT;
CLOSE cur;
select * from temp order by id;
drop table temp;
END
This is how I usually do, I hope it might work for you as well:
SELECT t.TABLE_NAME FROM information_schema.key_column_usage kcu
RIGHT JOIN information_schema.Tables t
ON t.TABLE_NAME=kcu.TABLE_NAME
WHERE t.TABLE_SCHEMA='<name_of_your_schema>' GROUP BY t.TABLE_NAME ORDER BY t.TABLE_TYPE DESC, COUNT(TRUE) DESC;
Selects tables ordered by their dependency count, most dependant tables will appear first, least dependant will appear last. Notice I ask for views before all, because, they're usually more dependant on tables than other tables, but they don't usually have any foreign keys.