I have one table which stores some records as JSON. I'm trying to increment the numeric value of a key from JSON column. Below is the query I'm executing in Laravel using DB facade.
DB::connection()
->select("
SELECT CAST(JSON_EXTRACT(consumed, '$.max_users') AS INT) INTO #tmp
FROM subscriptions
WHERE `id` = '2';
UPDATE subscriptions
SET consumed = JSON_SET(consumed, '$.max_users', #tmp+1)
WHERE `id` = '2';
");
It works perfectly ok in PhpMyAdmin however when attempting to execute it from Laravel it throws an exception, that SQL syntax is incorrect (where is it incorrect?).
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'UPDATE subscriptions SET consumed = JSON_SET(consumed, '$.max_users', ' at line 5 (SQL: SELECT CAST(JSON_EXTRACT(consumed, '$.max_users') AS INT) INTO #tmp FROM subscriptions WHEREid= '2'; UPDATE subscriptions SET consumed = JSON_SET(consumed, '$.max_users', #tmp+1) WHEREid= '2'; )
My suspicion is select() does not allow two different queries to be ran at once. However in this particular case I need them so in order to make use of #tmp variable. I could retrieve the value of first query and pass it to the second one using PHP, but for the sake of argument, how to run two DB selects at once in Laravel? 🤔
You can use a single update query to increment this value:
UPDATE subscriptions
SET consumed = JSON_SET(consumed, '$.max_users', CAST(JSON_EXTRACT(consumed, '$.max_users') AS INT) + 1)
WHERE `id` = '2';
Related
I have a PHP 7.3 project which connects via PDO to a MySQL database or a MSSQL database, depending on being run on Linux or Windows.
I want to insert a new values into a table, if the unique value is not yet in that table. If it is already in the table, I want to update the non-unique values.
I searched a lot of docs and SO posts, also, but I couldn't find a syntax, which does that in one query for both database types.
SQL Server query:
IF (EXISTS (SELECT * FROM failed_logins_ip_address WHERE ip_address = 'xxx'))
BEGIN
UPDATE failed_logins_ip_address
SET attempts_count = attempts_count + 1, attempt_datetime = CURRENT_TIMESTAMP
WHERE ip_address = 'xxx'
END
ELSE
BEGIN
INSERT INTO failed_logins_ip_address (ip_address, attempts_count, attempt_datetime)
VALUES ('xxx', 1, CURRENT_TIMESTAMP)
END
MySQL query:
INSERT INTO failed_logins_ip_address (ip_address, attempts_count, attempt_datetime)
VALUES ('xxx', 1, CURRENT_TIMESTAMP)
ON DUPLICATE KEY
UPDATE attempts_count = attempts_count + 1, attempt_datetime = CURRENT_TIMESTAMP
'ip_addess' column is unique, and the table structure is identical for both MSSQL and MySQL.
Is there a syntax, which can do an IF INSERT ELSE UPDATE in both database types?
Yes, I do (PDO) parameter binding, xxx is just to shorten the code snippet.
Yes, I could use identical syntax if I did it in two queries (first select, then insert or update) but I want to avoid (hopefully) unnecessary queries.
No, I do not want to insert every login attempt so I do not need the update anymore because I do not need this data.
If the REPLACE approach would work: this does not update, it deletes and inserts, which I also do not want.
My current solution: I check in PHP for the current database type and switch/case the query strings. It is clean but one string is even less smelly ;-)
UPDATE:
I changed the MSSQL query around: from of IF NOT EXISTS TO IF EXISTS to improve the efficiency. UPDATE will occur a lot more often than INSERT, so in most of the cases, only the first (sub)query will be executed.
After digging deeper, I found this post by a Derek Dieter, which describes how to replace SQL Server's IF EXISTS ELSE by WHERE EXISTS:
https://sqlserverplanet.com/optimization/avoiding-if-else-by-using-where-exists
The WHERE EXISTS syntax seems to be the same in MySQL and MSSQL.
Derek Dieter's example, with IF EXSISTS:
IF NOT EXISTS (SELECT 1 FROM customer_totals WHERE cust_id = #cust_id)
BEGIN
INSERT INTO customer_totals
(
cust_id,
order_amt
)
SELECT
cust_id = #cust_id
,order_amt = #order_amt
END
ELSE
UPDATE customer
SET order_amt = order_amt + #order_amt
WHERE cust_id = #cust_id
END
Derek Dieter's example, with WHERE EXISTS:
INSERT INTO customer_totals
(
cust_id,
order_amt
)
SELECT TOP 1 — important since we’re not constraining any records
cust_id = #cust_id
,order_amt = #order_amt
FROM customer_totals ct
WHERE NOT EXISTS — this replaces the if statement
(
SELECT 1
FROM customer_totals
WHERE cust_id = #cust_id
)
SET #rowcount = ##ROWCOUNT — return back the rows that got inserted
UPDATE customer
SET order_amt = order_amt + #order_amt
WHERE #rowcount = 0
AND cust_id = #cust_id — if no rows were inserted, the cust_id must exist, so update
I still have to test it, though, in MySQL. I'll update this post and add the code, if it works.
If you are using PHP, then you are calling the code through an interface. You can do the following:
Create a unique index on ip_address.
Attempt to insert a new row. This will fail if the row already exists.
If the insert fails (particularly with a duplicate key error), then update the existing row.
However, your goal of trying to have the same code in both databases is . . . just not going to work very well. The two databases are rather different. Perhaps you should consider constructing stored procedures in each database to do what you want and then calling those stored procedures.
[EDIT] Removed parametrized table name and altered values of order and minimum rating count to distinguish values [/EDIT]
I have a simple table with following create table
CREATE TABLE ratings_cache (
id INT NOT NULL AUTO_INCREMENT,
movie_id INT NOT NULL,
movie_title VARCHAR(128),
rating DECIMAL(5,4),
rating_count INT DEFAULT 0,
PRIMARY KEY (`id`)
);
Now, I want to query it using Doctrine with some parameters (app is Silex application of course):
$sql = "
SELECT ratings_cache.*
FROM :table_name
WHERE rating_count > :min_rankings
ORDER BY rating DESC, rating_count DESC
LIMIT :limit";
$stmt = $this->app['db']->prepare($sql);
$stmt->bindValue(':min_rankings', $this->minRanks);
$stmt->bindValue('limit', $this->limit);
return $stmt->execute()->fetchAll();
As you can see the query in this form is simple and it runs smoothly when I run it through Mysql console but when I run the code above I get following error:
An exception occurred while executing '
SELECT *
FROM ratings_cache
WHERE rating_count > :min_rankings
ORDER BY rating DESC, rating_count DESC
LIMIT :limit' with params [20, 30]:
SQLSTATE[42000]: Syntax error or access violation: 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 ''30'' at line 5
I realize the problem might be with something trivial but I am not very used to Doctrine (usualy prefer handling queries on my own) and simply don't know what might be wrong here...
I have table images with ID's starting from 1 to 5000. On the page they are showed straight from 1 to 5000. What I wonder is it possible to scramble the records inside database table not to query them by RAND() on the page because they will be random on every refresh. I don't want this.
For example id(1) to become id(343).. id(453)->id(4444) and so on.. just scrambling them.
UPDATE:
This is the query which I'm trying
alter table images add column randorder double;
update table images
set randorder = rand();
create index idx_table_randorder on table(randorder);
When is executed I got column randorder with NULL but the query return again
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 'table images set randorder = rand()' at line 1
How about this?
alter table add column randorder double;
update table
set randorder = rand();
create index idx_table_randorder on table(randorder);
Then you can "shuffle" the records by doing:
select t.*
from table t
order by randorder;
This will use the index so it will be fast. It is stable, so it will work by paging. When you want to change the ordering, you can use the update.
Save the rows into an array. Then run:
shuffle($array);
See http://php.net/manual/en/function.shuffle.php
I’m running this SQL query:
IF EXISTS (SELECT * FROM score WHERE username='ms')
UPDATE score SET score='30' WHERE username='ms'
ELSE
INSERT INTO score (username,score) VALUES ('ms','30')
but I keep getting this syntax 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 IF EXISTS (SELECT * FROM score WHERE username='ms') UPDATE score SET score='30' at line 1
What am I doing wrong?
if as control flow is only allowed in programming blocks in stored procedures, functions, and triggers. This should not be confused with the function if(), a non-standard MySQL extension that is allowed in any expression.
However, you can do what you want using replace or on duplicate key update. First, create a unique index on username:
create unique index idx_score_username on score(username);
Then do the insert as:
INSERT INTO score (username, score)
VALUES ('ms', 30)
ON DUPLICATE KEY UPDATE score = VALUES(score);
Note that I removed the single quotes from '30'. If score1 is numeric (a reasonable assumption I think), then use a numeric constant rather than a string constant.
I've finally gotten my queries ready to insert into code but now I'm getting an error when running the whole query. I believe it has to do with the drop table function. I originally had them inline and then read that I should remove it and add at the beginning of the query like so:
$query = $this->db->query("DROP TABLE IF EXISTS resultx;");
$query = $this->db->query("DROP TABLE IF EXISTS resulty;");
$query = $this->db->query("
CREATE TEMPORARY TABLE resultx AS
select *, CONCAT(Credit,'_',OrderStat) as consol from (..........
I am creating two temp tables and then joining them in the last query. I am not sure how to put that second DROP temp table back into the full query or if that's even the right way to go.
The error that I'm getting is:
A Database Error Occurred
Error Number: 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 'CREATE TEMPORARY TABLE resulty AS select packetDeet,Sales,SaleDate, UserID,Lead' at line 15
Query:
CREATE TEMPORARY TABLE resultx AS
select
*,
CONCAT(Credit,'_',OrderStat) as consol
FROM
( select
packetDetailsId, GROUP_CONCAT(Credit) AS Credit,
GROUP_CONCAT(AccountNum) AS AccountNum,
GROUP_CONCAT(OrderStat) AS OrderStat
FROM
( SELECT
pd_extrafields.packetDetailsId,
CASE WHEN
pd_extrafields.ex_title LIKE ('%Credit%')
THEN pd_extrafields.ex_value
ELSE NULL
END as Credit,
CASE WHEN
pd_extrafields.ex_title LIKE ('%Account%')
THEN pd_extrafields.ex_value
ELSE NULL
END as AccountNum,
CASE WHEN
pd_extrafields.ex_title LIKE ('%Existing%')
THEN pd_extrafields.ex_value
ELSE NULL
END as OrderStat
FROM pd_extrafields
) AS myalias
GROUP BY packetDetailsId
)as TempTab;
CREATE TEMPORARY TABLE resulty AS select packetDeet,Sales,SaleDate, .........
Please let me know if this makes sense or I need to update question with more information.
If you are trying to execute both queries in one call to $this->db->query() the problem is probably that your database library does not permit multiple queries.
To see if that is the problem, you should split them up in two separate queries.