Alias a mysql table for a session? - mysql

I know you can give an alias to a table for a query, like SELECT 1 FROM table AS t but is there a way to give an alias to a table for a session?
The use case is I have a "query pattern" that should apply to 3 different tables. So I would like to make only one query, using a table alias, and then tell mysql "Execute this query considering that is alias is table1, then execute the same query considering the alias is table2,..."
The use case:
INSERT INTO aliasedTable (id, value) VALUES (1,1)
The tables
CREATE TABLE table1 (id INT UNSIGNED, value TINYINT UNSIGNED)
CREATE TABLE table2 (id INT UNSIGNED, value TINYINT UNSIGNED)
CREATE TABLE table3 (id INT UNSIGNED, value TINYINT UNSIGNED)
The "supposed" syntax
ALIAS table1 AS aliasedTable;
INSERT INTO aliasedTable (id, value) VALUES (1,1)
ALIAS table2 AS aliasedTable;
INSERT INTO aliasedTable (id, value) VALUES (1,1)
ALIAS table3 AS aliasedTable;
INSERT INTO aliasedTable (id, value) VALUES (1,1)
What I thought of, is making a updatable VIEW of the table, but there is no such thing like CREATE TEMPORARY VIEW .... And using a CREATE TEMPORARY TABLE aliasedTable AS (SELECT * FROM table1) would create a table copy, instead of inserting in the original table.
Any suggestion?
Note that I have such case in a PHP code, but also in a procedure:
<?php
$query = 'INSERT INTO aliasedTable (id, value) VALUES (1,1)';
foreach (array('table1', 'table2', 'table3') AS $t) {
// Ideally, I would like to avoid string concat here and tell MySQL
// something like 'ALIAS :placeholder AS aliasedTable', ['placeholder' => $t]
$pdo->query('ALIAS ' . $table1 . ' AS aliasedTable');
$pdo->query($query);
}
or
SET #tables := '["table1","table2","table3"]';
SET #i := JSON_LENGTH(#tables) - 1;
WHILE (#i >= 0) DO
SET #table := JSON_UNQUOTE(JSON_EXTRACT(#tables, CONCAT('$[', #i, ']')));
ALIAS #table AS aliasedTable;
CALL inserting();
SET #i := #i - 1;
END WHILE;
where
CREATE PROCEDURE inserting()
BEGIN
INSERT INTO aliasedTable (id, value) VALUES (1, 1);
END$$

One thing that you could do is switch to a dynamic SQL statement and pass the table name as an input argument to the inserting function:
CREATE PROCEDURE inserting( IN tbl_name VARCHAR(25) )
BEGIN
SET #stmt = CONCAT('INSERT INTO ', tbl_name, ' (id, value) VALUES (1, 1)');
PREPARE stmt FROM #stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Depending on your call this will insert data to appropriate table. As an example:
CALL inserting('table1');
will execute following statement:
INSERT INTO table1 (id, value) VALUES (1,1);
Then from PHP you could call this procedure for each element in your array passing the current element as an argument to the function.

Related

How to find the sum of all the numeric values present in a MySQL table's column which has datatype as varchar and are separated by commas

I have a table which has been created using the following query
create table damaged_property_value
(case_id int, property_value varchar(100) );
insert into damaged_property_value (1,'2000'),(2,'5000,3000,7000');
The problem is I need to find the total value of all the properties that have been damaged.
I am writing the following query to return the sum:
select SUM(cast(property_value as unsigned)) from damaged_property_value;
It returns the sum as 7000, i.e , 2000+5000. It is not considering the value of property which are separated by commas.
Note that 5000,3000 and 7000 are values of three different properties that have been damaged in a particular case. It should have produced 17000 as an answer.
How to solve this problem.
Please help!
As was said, the best solution would be to fix the data structure.
Now, just for the fun of solving the problem, and after much research, I managed to do the following (it requires the case_id to be sequential, starting at 1) that calculates the values of the property_value strings and puts them into the new actual_value field.
drop table if exists damaged_property_value;
create table damaged_property_value
(case_id int primary key, property_value varchar(100), actual_value int );
insert into damaged_property_value (case_id, property_value) values (1,'2000'),(2,'5000,3000,7000'),(3, '7000, 2000'),(4, '100,200,300,400,500,600');
drop procedure if exists Calculate_values;
DELIMITER $$
CREATE PROCEDURE Calculate_values()
BEGIN
DECLARE count INT;
SET count = 1;
label: LOOP
select
concat('update damaged_property_value set actual_value = ',
replace((select property_value from damaged_property_value where case_id = count), ",", "+"),
' where case_id = ', count, ';')
into #formula;
#select #formula;
prepare stmt from #formula;
execute stmt;
deallocate prepare stmt;
SET count = count +1;
IF count > (select count(*) from damaged_property_value) THEN
LEAVE label;
END IF;
END LOOP label;
END $$
DELIMITER ;
CALL Calculate_values();
select * from damaged_property_value;
/* select SUM(actual_value) from damaged_property_value; */

How to `SELECT FROM` a table that is a part of a query itself using MySQL?

Say, if I have multiple tables that have the same schema:
CREATE TABLE `tbl01`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
CREATE TABLE `tbl02`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
CREATE TABLE `tbl03`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` TINYTEXT,
`data` INT
);
-- etc. ------------------
INSERT INTO `tbl01` (`name`, `data`) VALUES
('row 1', 1),
('row 2', 1),
('row 3', 3);
INSERT INTO `tbl02` (`name`, `data`) VALUES
('cube', 1),
('circle', 0);
INSERT INTO `tbl03` (`name`, `data`) VALUES
('one', 1);
and then one table that contains names of all other tables in one of its columns:
CREATE TABLE `AllTbls`
(
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`tblnm` VARCHAR(64) NOT NULL UNIQUE,
`desc` TINYTEXT,
`flgs` BIGINT UNSIGNED
);
INSERT INTO `AllTbls` (`tblnm`, `desc`, `flgs`) VALUES
('tbl01', 'Table 1', 0),
('tbl02', 'Table two', 1),
('tbl03', '3rd table', 0);
So if I want to write a query to retrieve contents of AllTbls and also in one column to include count of rows in each of corresponding tables, I thought the following would be the way to do it:
SELECT *, `tblnm` as TblName, (SELECT COUNT(*) FROM TblName) as cntRws
FROM `AllTbls` ORDER BY `id` ASC LIMIT 0,30;
But this returns an error:
#1146 - Table 'database.TblName' doesn't exist
I know that I can do this in multiple queries (using a loop in a programming language), but is it possible to do it in one query?
PS. I'm using MySQL v.5.7.28
The simple answer is: "you can't"
Table names are not supposed to be used like variables, to hold data, in this way. What you're supposed to have is one table:
tblContractCounts
Client, ContractCount
-------------------
IBM, 1
Microsoft, 3
Google, 2
Not three tables:
tblIBMContractCounts
ContractCount
1
tblMicrosoftContractCounts
ContractCount
3
tblGoogleContractCounts
ContractCount
2
If your number of tables is known and fixed you can perhaps remedy things by creating a view that unions them all back together, or embarking on an operation to put them all into one table, with separate views named the old names so things carry in working til you can change them. If new tables are added all the time it's a flaw in the data modelling and need to be corrected. In that case you'd have to use a programming language (front end or stored procedure) to build a single query:
//pseudo code
strSql = ""
for each row in dbquery("Select name from alltbls")
strSql += "select '" + row.name + "' as tbl, count(*) as ct from " + row.name + " union all "
next row
strSql += "select 'dummy', 0"
result = dbquery(strSql)
It doesn't have to be your front end that does this - you could also do this in mysql and leverage the dynamic sql / EXECUTE. See THIS ANSWER how we can concatenate a string using logic like above so that the string contains an sql query and then execute the query. The information schema will give you the info you need to get a list of all current table names
But all you're doing is working around the fact that your data modelling is broken; I recommend to fix that instead
ps: the INFORMATION_SCHEMA has rough counts for tables with their names, which may suffice for your needs in this particular case
select table_name, table_rows from infornation_schema.tables where table_name like ...
I managed to solve the problem using the following stored procedure.
-- DROP PROCEDURE sp_Count_Rows;
Delimiter $$
CREATE PROCEDURE sp_Count_Rows()
BEGIN
DECLARE table_name TEXT DEFAULT "";
DECLARE finished INTEGER DEFAULT 0;
DECLARE table_cursor
CURSOR FOR
SELECT tblnm FROM alltbls;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN table_cursor;
DROP TABLE IF EXISTS RowsCount;
CREATE TABLE IF NOT EXISTS RowsCount(Tlbnm text, ctnRws int);
table_loop: LOOP
FETCH table_cursor INTO table_name;
IF finished = 1 THEN
LEAVE table_loop;
END IF;
SET #s = CONCAT("insert into RowsCount select '", table_name ,"', count(*) as cntRws from ", table_name);
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END LOOP table_loop;
CLOSE table_cursor;
SELECT * FROM RowsCount;
DROP TABLE RowsCount;
END
$$
And then when you call the procedure
CALL sp_Count_Rows();
You get this result

SELECTing the column names for another SELECT - possible?

I have a table t1 with a 'foo' column, and records containing 'bar' and 'baz' in column 'foo'. I also have a table t2 with columns 'bar', 'baz' and 'quux'. I want to do something like the following:
SELECT (SELECT foo from t1) FROM t2;
that is, get the two column names from t1 and query those columns of t2. If I try to do this with MonetDB, I get an error message:
cardinality violation (2>1)
so,
is there some other way to do this with MonetDB?
is this possible in MySQL? other DBMSes?
Example queries (or non-query directives followed by a query) are welcome.
first you need to select the columns into a variable and then use that variable to get tables
something like the following (it is not the actual implementation)
declare #colname varchar(250)
select #colname=foo from t1
select #colname from t2
this may be help.
This is how you can accomplish it in SQL Server
-- Create #T1 which contains names of columns and #T2 which contains the actual columns
CREATE TABLE #T1(colname nvarchar(max))
INSERT INTO #T1 (colname) VALUES ('b')
INSERT INTO #T1 (colname) VALUES ('c')
CREATE TABLE #T2(a int IDENTITY(1,1), b DATETIME DEFAULT GETDATE(), c NVARCHAR(max) DEFAULT 'blah')
INSERT INTO #T2 DEFAULT VALUES
INSERT INTO #T2 DEFAULT VALUES
INSERT INTO #T2 DEFAULT VALUES
--#dSQL will contain the actual SQL string to be executed
DECLARE #dSQL nvarchar(max) = ' '
SELECT #dsql = CONCAT(#dsql,'[',colname,'],') FROM #T1
SELECT #dsql = CONCAT('SELECT',LEFT(#dsql,LEN(#dsql)-1),' FROM #T2')
--You can see the SQL query being executed
PRINT #dsql
--Actually execute it
exec sp_executesql #dsql
MS Sql dynamic sql example.
create table t1(foo varchar(100))
insert t1(foo)
values('bar,baz');
create table t2(bar int, baz int)
insert t2 (bar, baz)
values
(1,100),
(3,300);
declare #cmd varchar(max);
select #cmd= 'select '+ (select top(1) foo from t1) + ' from t2';
EXEC (#cmd);
Result
bar baz
1 1 100
2 3 300
Or may be foo contains column names in different rows, not exactly clear from your question.
create table t1(foo varchar(100))
insert t1(foo)
values('bar'),('baz');
create table t2(bar int, baz int)
insert t2 (bar, baz)
values
(1,100),
(3,300);
declare #cmd varchar(max);
select #cmd= 'select '+ stuff((select ','+ foo from t1 for xml path('')),1,1,'') + ' from t2';
EXEC (#cmd);

Parameterized nested query

I'm trying to parameterize the following insert with a nested select.
INSERT IGNORE INTO table1 (creation_timestamp, str1, str2)
(SELECT now(), "param1", str2 FROM table2 WHERE key = "param2");
I'd like something like
INSERT IGNORE INTO table1 (creation_timestamp, str1, str2)
(SELECT now(), ?, str2 FROM table2 WHERE key = ?)
VALUES ("param1", "param2");
Anyone know how I can accomplish something like this?
Not exactly the same, but very similar.
You can use prepared statements:
http://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
Example:
PREPARE soExample
FROM 'INSERT
INTO usr (id, username, profile_pic)
VALUES (NULL, ?, (SELECT name
FROM customers
WHERE id = ?
LIMIT 1))';
SET #uname = "someUserNameForTheExample";
SET #id = "1";
EXECUTE soExample USING #uname, #id;
Or you can user procedure or/and functions as well
FUNCTION
DROP FUNCTION IF EXISTS insertExample$$
CREATE FUNCTION insertExample(userNameVar VARCHAR(255), uID INT(11)) RETURNS BOOLEAN
BEGIN
INSERT
INTO usr (id, username, profile_pic)
VALUES (NULL, userNameVar, (SELECT name
FROM customers
WHERE id = uID
LIMIT 1));
IF ROW_COUNT() > 0 THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END$$
FUNCTION USE
SELECT insertExample("SomeUsername" 2);
Perhaps you should start by reading https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html. As Mysql Functions enables parameterized input for pre-build queries.

GROUP CONCAT REPLACE sep strings

I am using the below statement to pull a unique value off a table from a field that is separated with "|". It looks like this: "40|180|408|360|40|1s66|80|59" My problem is that I can't seem to get this statement to allow me to pull a primary key field, id, and assign it to each unique item from the field string so I can actually use it later. Ideally, ID # 27 should be assigned to each of the values on this temp table from "40|180|408|360|40|1s66|80|59". Can anyone help with the below statement to allow me to insert and assign field id to this from table BlogImageBundle?
CREATE TEMPORARY TABLE test (postId INT(11), val CHAR(255));
SET #S1 = CONCAT("INSERT INTO test (val) VALUES ('",REPLACE((SELECT GROUP_CONCAT( DISTINCT `images`)
AS data FROM `BlogImageBundle`), "|", "'),('"),"');");
PREPARE stmt1 FROM #s1;
EXECUTE stmt1;
SELECT DISTINCT(val) FROM test;
This works but ONLY for one record. You will need to add another while loop that keeps track of the rowcount to deal with multiple records : http://sqlfiddle.com/#!6/d62f7/1
CREATE TABLE #Test
(
ID INT,
Initial VARCHAR(MAX)
)
CREATE TABLE #Test2
(
ID INT,
Final VARCHAR(MAX)
)
INSERT INTO #Test VALUES(27,'40|180|408|360|40|1s66|80|59')
DECLARE #String VARCHAR(MAX)
SET #string = (SELECT Initial FROM #Test)
DECLARE #StringInput VARCHAR(MAX)
SET #stringInput = (SELECT Initial FROM #Test)
WHILE LEN(#StringInput) > 0
BEGIN
SET #String = LEFT(#StringInput,ISNULL(NULLIF(CHARINDEX('|', #StringInput) - 1, -1),LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,ISNULL(NULLIF(CHARINDEX('|', #StringInput), 0),LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #Test2 (ID, Final)
SELECT ID,#string FROM #Test
END