Delete the dupliacte using TOP in MYSQL query throws Error Code - mysql

I just used TOP syntax to delete the duplicate value from table. But it returns error code.
Query:
DELETE top(SELECT COUNT(*)-1 FROM tests WHERE test_name='WALLET_01' AND product_id=25)FROM tests WHERE test_name='WALLET_01' And product_id=25;
Error Code:
Error Code: 1064 You have an error in your SQL syntax;
Executed alone below query return int value as 1:
SELECT COUNT(*)-1 FROM tests WHERE test_name='WALLET_01' AND product_id=25;
Anybody can help me Why I'm getting syntax error?

You could not use a subquery in Top
Top syntax
[
TOP (expression) [PERCENT]
[ WITH TIES ]
]
expression
Is the numeric expression that specifies the number of rows to be returned. expression is implicitly converted to a float value if PERCENT is specified; otherwise, it is converted to bigint.
We can see it didn't support subquery in TOP only expression
EDIT
I saw you change your use dbms.
Mysql didn't support TOP but you can use LIMIT get the limit row.
If there isn't PK in your table, you can try to use dynamic SQL get to decide how many rows you want to delete in the runtime.
Schema (MySQL v5.6)
CREATE TABLE tests(
test_name VARCHAR(50),
product_id int
);
INSERT INTO tests VALUES('WALLET_01',25);
INSERT INTO tests VALUES('WALLET_01',25);
SET #sql = NULL;
SET #Rn = NULL;
SELECT
(COUNT(*)-1)
INTO #Rn
FROM tests
WHERE test_name='WALLET_01' AND product_id=25;
SET #sql = CONCAT('DELETE FROM tests WHERE test_name=''WALLET_01'' AND product_id=25 limit ',#Rn);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Query #1
SELECT * FROM tests;
| test_name | product_id |
| --------- | ---------- |
| WALLET_01 | 25 |
View on DB Fiddle

Related

Get condition of WHERE clause from table

I have table1 which has only 2 columns: id and condition. For example:
id condition
--------------------
100 caption LIKE "%xyz%"
200 tag=5
300 color>153
...
The user sends an id to the server, and a specific select query must be run on table2 based on the condition of that id. For example, if id 100 is sent to server, then this query must be run:
SELECT * FROM table2 WHERE caption LIKE "%xyz%"
How to get the condition from table1 and run the query with that condition? I have already tried this:
SELECT * FROM table2 INNER JOIN table1 AS t1 ON t1.id=... WHERE t1.condition
However, I get the following warning with no result.
Warning: #1292 Truncated incorrect DOUBLE value: 'caption LIKE "%xyz%"'
This is possible in MySQL 5+ using prepared statements. You can create a procedure with a condition id as an argument:
DELIMITER //
CREATE PROCEDURE get_from_table2_by_condition_id(IN conditionId bigint)
BEGIN
SET #condition = NULL;
SELECT cond
INTO #condition
FROM table1
WHERE id = conditionId;
SET #sql = CONCAT('SELECT * FROM table2 WHERE ', COALESCE(#condition, 'FALSE'));
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END//
DELIMITER ;
CALL get_from_table2_by_condition_id(100);
See a working example in this fiddle
I don't particularly recommend storing SQL code as you are describing.
But you have a client and a server. The solution is simply to query table1 for the condition and then construct the query that you want from that. This requires two queries.
You could set up a stored procedure inside the database that uses dynamic SQL for the same purpose.
There are several reasons why this is not desirable:
The code can introduce syntax errors which are quite hard to debug.
The code is subject to SQL injection, depending on the security features around table1.
Changes to the underlying tables might invalidate the conditions.
What is an alternative? One possibility is to create separate views for the different conditions. Or, just create one query and pass in parameters:
select t2.*
from table2 t2
where (caption like :caption or :caption is null) and
(tag = :tag or :tag is null) and
(color > :color or :color is null);
That is not possible, and if it where possible that would be a huge stored SQL injection security hole. To be able to do this, you will need something that will parse the stored condition and evaluate the condition on the fly. As far as I'm aware, MySQL has nothing built in for this.

MySQL create a dynamic table name in a Join statement

long time user, first time poster.
I have 2 tables;
a1_watchlists {id(PK),name,date}
a1_watchlist {id(PK),watchlists_id(FK(a1_watchlists.id)),company_name,asx_code,date}
I also have 2000 other tables that have been created with the name 'asx_'+[asx_code] (where asx_code is pulled from another table)
this table looks like;
asx_[asx_code] {date(PK),open,high,low,close,volume}
I want to select all from a1_watchlists and a1_watchlist and then select the latest date from the asx_[asx_code] table using the value from a1_watchlist.asx_code to generate the [asx_code] part of the table name.
The problem I have is that I want to use the value from a1_watchlist.asx_code as the table name prepending the string 'asx_' to this first.
Closest I have been able to get is;
DECLARE #TableName VARCHAR(100)
SELECT *
FROM a1_watchlist AS wl
JOIN a1_watchlists AS wls
ON wls.id = wl.watchlists_id
SET #TableName = 'asx_' + wl.asx_code
INNER JOIN (SELECT MAX(date),open,high,low,close,volume,amount_change,percent_change FROM #TableName)
This currently give the error:
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE #TableName VARCHAR(100)
SELECT *
FROM a1_watchlist AS wl
' at line 1
The expected colums I need in the final result would be:
wl.id,wl.watchlists_id,wl.company_name,wl.asx_code,asx_[asx_code].date,asx_[asx_code].open,asx_[asx_code].high,asx_[asx_code].low,asx_[asx_code].close,asx_[asx_code].volume
Let me know if you require more information.
I'm not going to speak to what to do in the case where you have 2000+ tables that start with asx+ some code... (i live in a town with multiple bridges) or even whether what you're doing is the best way to get where you want to go. BUT, it does look like you're attempting to concatenate things together and create a dynamic statement. If that sounds right, then I'd recommend you look into prepared statements. Like the following. Hope this helps.
DELIMITER $$
DROP PROCEDURE IF EXISTS prRetrieveAllFromTable$$
CREATE PROCEDURE prRetrieveAllFromTable(tableName VARCHAR(64))
BEGIN
SET #s = CONCAT('SELECT * FROM ',tableName );
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
CALL prRetrieveAllFromTable('calendar');
http://dev.mysql.com/doc/refman/5.5/en/sql-syntax-prepared-statements.html
How To have Dynamic SQL in MySQL Stored Procedure

using mysql variable to hold comma separated value to be used for where in clause

I have to run a query like this (query 1) -
select something from sometable where someId in (1,2,3)
I would like to keep a variable for the IDs part, like this (query 2) -
set #myIds = "1,2,3";
select something from sometable where someId in (#myIds);
But this does not give the expected result (gives an empty result set), and no query error as well.
I checked that if I wrap the comma separated IDs inside quotes, the query results an empty result set (query 3) -
select something from sometable where someId in ("1,2,3");
I guess when I am using variable #myIds like I showed above (query 2), it is evaluating to the above query (query 3).
You need to have a dynamic sql on this,
SET #myIds = '1,2,3';
SET #sql = CONCAT('select something from sometable where someId in (',#myIds,')');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
The proper (and also more complicated) way to do that would be a temp table:
DROP TEMPORARY TABLE IF EXISTS `some_tmp_table`
CREATE TEMPORARY TABLE `some_tmp_table` (
`id` INT(10) UNSIGNED NOT NULL
) ENGINE=MEMORY #memory engine is optional...
Insert your ID's the temp table
INSERT INTO some_tmp_table VALUES (1),(2),(3)...
and then use a JOIN instead of IN().
SELECT something
FROM sometable s
JOIN some_tmp_table ts ON ts.id = s.someId
The other way is to use dynamic SQL as the other answer suggests. It might be simpler for you to generate the dynamic SQL in your app, but you can do it in MySQL too.

Why is mysql caching the column names of a temporay table that is deleted?

I've reduced my issue down to this simple SP. The column names are getting cached in the SELECT * at the end. I have no idea why or how to stop it. I tried adding SQL_NO_CACHE but that makes no difference.
DROP TABLE IF EXISTS foo;
CREATE TABLE foo(
col1 int,
col2 int);
INSERT INTO foo VALUES(1,2),(3,4),(5,6);
DROP PROCEDURE IF EXISTS mysp;
DELIMITER ;;
CREATE DEFINER=root#localhost PROCEDURE mysp(c INT)
BEGIN
DROP TABLE IF EXISTS mydata;
SET #mycol='col1';
IF c > 0 THEN SET #mycol:='col2';
END IF;
SET #s=CONCAT('CREATE TEMPORARY TABLE mydata AS SELECT ', #mycol, ' FROM foo');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- The following select call fails on 2nd and subsequent executions of the SP
SELECT SQL_NO_CACHE * FROM mydata;
SELECT "Please see new temp table mydata" as Result;
END ;;
DELIMITER ;
Version
mysql> SELECT VERSION();
+------------+
| VERSION() |
+------------+
| 5.5.15-log |
+------------+
1 row in set (0.00 sec)
First run works fine as expected
mysql> CALL mysp(0);
+------+
| col1 |
+------+
| 1 |
| 3 |
| 5 |
+------+
3 rows in set (0.17 sec)
+----------------------------------+
| Result |
+----------------------------------+
| Please see new temp table mydata |
+----------------------------------+
1 row in set (0.17 sec)
Query OK, 0 rows affected (0.17 sec)
Now if I try and run it again using the other column
mysql> CALL mysp(1);
ERROR 1054 (42S22): Unknown column 'qlgqp1.mydata.col1' in 'field list'
mysql> SELECT #mycol;
+--------+
| #mycol |
+--------+
| col2 |
+--------+
1 row in set (0.00 sec)
If I recreate the storedprocedure again its works
mysql> CALL mysp(1);
+------+
| col2 |
+------+
| 2 |
| 4 |
| 6 |
+------+
3 rows in set (0.18 sec)
+----------------------------------+
| Result |
+----------------------------------+
| Please see new temp table mydata |
+----------------------------------+
1 row in set (0.18 sec)
Query OK, 0 rows affected (0.18 sec)
But if I try switching back to the first column - even if I try dropping the temp table first - it still doesn't work
mysql> CALL mysp(0);
ERROR 1054 (42S22): Unknown column 'qlgqp1.mydata.col2' in 'field list'
mysql> DROP TABLE mydata;
Query OK, 0 rows affected (0.03 sec)
mysql> CALL mysp(0);
ERROR 1054 (42S22): Unknown column 'qlgqp1.mydata.col2' in 'field list'
mysql>
*Additional info asked for by eggyal. Also I tried this on another mysql version with same result. *
mysql> CALL mysp(1);
+------+
| col2 |
+------+
| 2 |
| 4 |
| 6 |
+------+
3 rows in set (0.20 sec)
+----------------------------------+
| Result |
+----------------------------------+
| Please see new temp table mydata |
+----------------------------------+
1 row in set (0.20 sec)
Query OK, 0 rows affected (0.20 sec)
mysql> describe mydata;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| col2 | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)
mysql> CALL mysp(0);
ERROR 1054 (42S22): Unknown column 'test.mydata.col2' in 'field list'
mysql> describe mydata;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| col1 | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)
Interesting development of a fix - changing the last few lines to a prepared statement works - but using exactly the same query as before.
-- The following select call fails on 2nd and subsequent executions of the SP
PREPARE stmt FROM 'SELECT SQL_NO_CACHE * FROM mydata';
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SELECT "Please see new temp table mydata" as Result;
MySQL is reusing the statement that was prepared on the previous execution. It's not really "caching" column names; what it's "caching" (if you will) is the prepared statement.
The easy workaround is to use dynamic SQL statement to gain control over the behavior, and avoid the reuse of the previously prepared statement:
SET #s=CONCAT('SELECT ',#mycol,' FROM mydata');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
It's not matter of the column names being "cached", or the results of the query being cached. It's a performance optimization; it's a matter of that statement already being prepared, within your session.
By using dynamic SQL, you can control when the statement is prepared (i.e. parsing the SQL text for syntax (statement formation, keywords, etc.), checked for semantics (object names exist, column names exist, user has required privileges, etc.), and preparing an execution plan.
With the static SQL, all of that happens on the first execution, and then MySQL hangs on to the prepared statement.
For performance reasons, we wouldn't want the overhead of a "hard parse" every time a static statement is executed. This is especially true on a function that gets called multiple times, from a SQL statement.
(NOTE: Oracle does the same thing, BUT, Oracle does a good job of marking prepared statements as INVALID whenever a referenced object is altered or dropped.)
MySQL opted not to do that, probably because of the overhead of tracking all the dependencies. And, in the vast majority of cases, that overhead is not required.
I think the lesson here is if you are going to use dynamic SQL to create a table that is going to have DIFFERENT columns in it, you are going to have to use dynamic SQL to query that table.
My recommendation is that you avoid using SELECT *, unless your statement is in complete control of the columns being returned, for example, from an inline view. Otherwise, SQL statements that use SELECT * are fundamentally broken... they may work now, but a change to a table (adding a column for example) will break an application.
Q: Please explain how it is not a bug.
It is not a bug because the SELECT statement in your stored procedure is really just shorthand for what is actually happening.
On the first execution of your procedure, MySQL is doing the parse of your query text, and preparing and executing a statement. Basically, equivalent to:
PREPARE s1 FROM 'SELECT * FROM mydata';
EXECUTE s1;
On the second execution of the procedure, MySQL is simply executing the statement that has been previously prepared. Basically, equivalent to:
EXECUTE s1;
On that second execution, you seem to be expecting MySQL to run the equivalent of:
DEALLOCATE PREPARE s1;
PREPARE s1 FROM 'SELECT * FROM mydata';
EXECUTE s1;
You can make a case that this is what MySQL should be doing on the second execution. You could argue that statements prepared during a previous execution of a procedure should be discarded, and re-parsed and re-prepared on subsequent executions.
It would not be wrong for a DBMS to do this. But there would be, as always, the consideration about the impact on performance.
You could also make a case that MySQL should track all the database objects that a particular prepared statement is dependent on. You could argue that whenever one of those database objects is dropped or altered, MySQL should invalidate all the prepared statements (and all other objects) that depend on the altered or dropped object. Again, it would not be wrong for a DBMS to do this. Some DBMSs (such as Oracle) do this quite well. But again, the developers of the DBMS also take performance into consideration when making these design and implementation decisions.
The bottom line is that MySQL does provide you with a way to make happen what you want to happen. It's just that the syntax in your procedure, what you are expecting to make it happen, doesn't actually make it happen.
first of all it is a temp table so really should not be expected to be there, 2nd - it is dropped
I think you are reading something different into the "TEMPORARY" keyword than is defined in the specification. A TEMPORARY table is really just like a regular table, except that it is visible only to the session that created it, and it will be automatically dropped when the MySQL session ends. (We also note that a TEMPORARY table is not displayed by a SHOW TABLES command, and does not appear in the information_schema views.)
As to which tables (TEMPORARY or otherwise) MySQL should expect "to be there", I don't believe the documentation really addresses that, except noting that when a SQL statement is executed, and that statement references an object that does not exist, MySQL will throw an exception.
The same behavior you observe with a TEMPORARY table, you will also observe with a non-TEMPORARY table. The issue is not related to whether the table is defined as TEMPORARY or not.
where does SELECT * compare to PREPARE s1 FROM SELECT *
Those two forms effectively follow the same code path. The first execution of a static SELECT * is effectively equivalent to:
PREPARE s1 FROM 'SELECT *';
EXECUTE s1;
(Note the absence of a DEALLOCATE statement following the exeuction.) On a subsequent execution, the statement is already prepared, so it's effectively equivalent to:
EXECUTE s1;
This is similar to what would happen if you were coding in PHP mysqli
$s1 = $mysqli->prepare("SELECT * FROM mydata");
$mysqli->execute($s1);
/* rename the columns in the mydata table */
$mysqli->execute($s1);
I understand this is relatively old (+6 months) but I encountered this problem in prepared statements, and the only way I got around it was to concatenate the field names and use a prepared statement that effectively calls "select *" but uses the actual field names. I'm using prepared statements to create a temporary table, and the only standard field is the first one, while the rest causes the caching problem on "Select *".
Select fields we want to use into a temp table
Iterate through table rows of field names, and on each iteration:
set sql01 = concat(sql01,',',sFieldName,''); (just fields) and: set sql02 = concat(sql02,',',sFieldName,' varchar(50) '); (fields + field type only, for create table statement)
create the output table:
set #sql = concat('CREATE TEMPORARY TABLE tOutput(FirstField varchar(50), ',sql02,');'); PREPARE STMT FROM #sql; EXECUTE STMT; DEALLOCATE PREPARE STMT;
At the end:
set #sql = concat('SELECT FirstField,',sql01,' FROM tOutput;'); PREPARE STMT FROM #sql; EXECUTE STMT; DEALLOCATE PREPARE STMT;

Unable to Create Prepared Statements in MySQL

I'm trying to create a prepared statement in MySQL that takes in a single parameter. When I try this on the command line I get a syntax error. However, when there are no variables in my prepared statement, I am able to create them fine. Below is a copy and paste of what I am seeing at the MySQL command prompt:
mysql> PREPARE state_name FROM "select * from ? limit 1";
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? limit 1' at line 1
mysql> PREPARE state_name FROM "select * from documents limit 1";
Query OK, 0 rows affected (0.00 sec)
Statement prepared
The version of MySQL I'm using is 5.0.77-log.
Is there a syntax error I'm not seeing? And are there any config parameters I have to set in order to get prepared statements to work?
You can't pass in table names as prepared statement parameters, I'm afraid, neither can you pass columns to group or sort by. The only thing you can parametrize are fields in where clauses or for updates:
PREPARE state_name FROM "select * from documents where id = ?";
PREPARE state_name FROM "update documents set field = ? where id = ?";
If you want to create a single prepare statement and use it for more than one table, you cand actually create a variable for every table, and then create the statement using this variable, as the MySQL manual explain it :
mysql> USE test;
mysql> CREATE TABLE t1 (a INT NOT NULL);
mysql> INSERT INTO t1 VALUES (4), (8), (11), (32), (80);
mysql> SET #table = 't1';
mysql> SET #s = CONCAT('SELECT * FROM ', #table);
mysql> PREPARE stmt3 FROM #s;
mysql> EXECUTE stmt3;
+----+
| a |
+----+
| 4 |
| 8 |
| 11 |
| 32 |
| 80 |
+----+
mysql> DEALLOCATE PREPARE stmt3;