I have the following query:
INSERT INTO `impressions` (`date`, `item_id`, `platform`, `country`) VALUES ('" . $date . "', '" . $item_id . "', '" . $platform . "', '" . $country . "') ON DUPLICATE KEY UPDATE `impressions` = `impressions` + 1
And the following array:
Array
(
[0] => 5
[1] => 2
[2] => 4
[3] => 17
)
The array basically consists of item_ids, which I want to insert/update in to the database.
When the query runs, I want it to check to see if there are any rows which are from: today, match the specificed item_id, match the specified platform and match the specified country.
For example:
2015-03-05 5 mobile US (new insert)
2015-03-05 2 mobile UK (new insert)
2015-03-05 5 mobile US (this would +1 impression from first one)
2015-03-05 17 desktop US (new insert)
2015-03-06 5 mobile US (this would create a new insert because the date doesn't exist)
I also want to pass an array of item_ids, as specified above using IN, to avoid multiple looping.
Right now we are currently doing it like this:
$item_ids = array('5', '2', '4', '17');
foreach($item_ids as $id){
$q = mysql_query("SELECT `id` FROM `impressions` WHERE `date` = '" . $date . "' AND `item_id` = '" . $id . "' AND `platform` = '" . $platform . "' AND `country` = '" . $country . "'");
if (mysql_num_rows($q)){
$r = mysql_fetch_array($q);
mysql_query("UPDATE `impressions` SET `impressions` = `impressions` + '1' WHERE `id` = '" . $r['id'] . "'");
} else {
mysql_query("INSERT INTO `impressions` (`date`, `item_id`, `platform`, `country`, `impressions`) VALUES('" . $date . "', '" . $id . "', '" . $platform . "', '" . $country . "', '1') ");
}
}
We basically want to convert this huge (and badly coded) loop, into 1 mySQL INSERT/UPDATE query which gets passed an array of item_ids to be used with IN.
I am really confused how I should approach this. Any help would be greatly appreciated.
UPDATE impressions = impressions + 1
needs to be
UPDATE impressions = VALUES(impressions) + 1
INSERT INTO `impressions` (
`date`,
`item_id`,
`platform`,
`country`,
`impressions`
)
VALUES (
'{$date}',
{$id},
'{$platform}',
'{$country}', '1')
ON DUPLICATE KEY
UPDATE `impressions` = VALUES(impressions) + 1
for this to work, see to it that the table impressions has a primary key to check with.
so that the on duplicate key it will update it
Related
There was a problem with the request. Any ideas?
The situation is as follows, there is a request
MERGE ore as T_Base USING ( SELECT
date
,sec
,shift
,id_ore
FROM ore
WHERE date = '2022-9-20'
AND sec = '2'
AND shift = '1') AS T_Source
ON T_Base.date = T_Source.date
AND T_Base.sec = T_Source.sec
AND T_Base.shift = T_Source.shift
AND T_Base.id_ore = T_Source.id_ore
WHEN MATCHED
THEN UPDATE
SET date = T_Source.date
,sec = T_Source.sec
,shift = T_Source.shift
,id_ore = '1'
WHEN NOT MATCHED
THEN INSERT (date, sec, shift, id_ore)
VALUES (T_Source.date, T_Source.sec, T_Source.shift, '1');
But for some reason it returns "0 rows affected".
Table structure:
[id] [int] IDENTITY(1,1) NOT NULL,
[date] [datetime] NULL,
[sec] [int] NULL,
[shift] [int] NULL,
[id_ore] [int] NULL
Values in the table:
id date sec shift id_ore
11637 2022-09-10 00:00:00.000 1 1 13
11636 2022-09-09 00:00:00.000 1 2 13
etc
Expected Result
When updated, no change
id date sec shift id_ore
11637 2022-09-10 00:00:00.000 1 1 13
1.1) When update, with changes (shift and id_ore updated )
id date sec shift id_ore
11637 2022-09-10 00:00:00.000 1 2(change) 27(change)
When new values are inserted:
id date sec shift id_ore
11638 2022-09-20 00:00:00.000 1 1 1
Sorry for this post. I misunderstood the MERGE construction. If there is no data in any of the tables, the operation will not be performed, since there will be nothing to match
If suddenly, someone encountered a similar, came to the following solution:
IF EXISTS (SELECT
date
,sec
,shift
FROM ore
WHERE date = '" . $y . "-" . $m . "-" . $d . "'
AND sec = '" . $sec . "'
AND shift = '" . $shift. "')
UPDATE ore
SET id_ore = '" . $nam_ryd . "'
WHERE date = '" . $y . "-" . $m . "-" . $d . "'
AND sec = '" . $sec . "'
AND shift = '" . $shift. "'
ELSE
INSERT ore (date, sec, shift, id_ore)
VALUES ('" . $y . "-" . $m . "-" . $d . "', '" . $sec . "', '" . $shift. "', '" . $nam_ryd . "')
$sql = "INSERT INTO `coupon` SET
`name` = 'FREECOUPON',
`coupon_code` = '" . $this->db->escape($couponCode) . "',
`discount` = '38',
`uses_total` = '22',
`status` = '1'
";
$this->db->query($sql);
$coupon_id = $this->db->getLastId();
$this->db->query("INSERT INTO coupon_cat SET coupon_id = '" . (int)$coupon_id . "', category_id = 79 ");
The query works, but I want to modify this
$this->db->query("INSERT INTO coupon_cat SET coupon_id = '" . (int)$coupon_id . "', category_id = 79 ");
I want to "mass pump" data into coupon_cat. I need to pump from category 30-180, but in different lines. Any idea how to do it ?
If I understand you clear you want to insert the same coupon_id to 150 category ids. You can insert all the rows in one query using loop to create all the values.
$coupon_id = (int)$this->db->getLastId();
$values = [];
for ($category_id = 30; $category_id <= 180; $category_id++) {
$values[] = '(' . $coupon_id . ',' . $category_id . ')';
}
$this->db->query('INSERT INTO `coupon_cat` (coupon_id, category_id) VALUES ' . implode(',', $values));
I have this current SQLite table:
CREATE TABLE Goal (
id INTEGER PRIMARY KEY,
Name CHAR(32) NOT NULL,
Image CHAR(256),
Target INTEGER NOT NULL,
Priority CHAR(32) NOT NULL
);
And am trying to create an integer id via PHP as follows:
$query = 'INSERT OR IGNORE INTO Goal (Name, Image, Target, Priority) VALUES'; // Query to insert goal-related info into Goal table
$query .= "('" . $name . "', '" . $image . "', '" . $target . "', '" . $priority . "');";
$addGoalDB -> makeQuery( $query );
$query = 'SELECT last_insert_rowid()';
$result = $addGoalDB -> makeQuery( $query );
$id = $result -> fetchArray(SQLITE3_ASSOC);
$id = $id['id'];
$query = 'INSERT OR IGNORE INTO Users_Goal (Email, id) VALUES';
$query .= "('" . $_SESSION['username'] . "', '" . $id . "');";
$addGoalDB -> makeQuery( $query );
But keep getting an undefined index error with
$id = $id['id'];
I am assuming the query is wrong but cannot find where my problem is. This link: http://alvinalexander.com/android/sqlite-autoincrement-insert-value-primary-key makes it look straightforward but does not work with my code. I would greatly appreciate any help with this.
You are trying to read a column named "id". But columns are named after what you have written in the SELECT clause, so the actual column name is something like last_insert_rowid().
Give the column a proper name:
$query = 'SELECT last_insert_rowid() AS id';
How to insert the same id in different tables at a time in one table. it is a primary key and autoincrement and in another table. It is a foreign key at a time. I have to insert in both the tables using OpenCrat.
$this->db->query("INSERT INTO "
. DB_PREFIX . "xyz
SET
boutiques_id = '" . (int)$this->customer->getId() . "',
boutique_customer_id = '" . $this->db->escape($data['boutique_customer_id']) . "',
ordered_date = '" . $this->db->escape($data['ordered_date']) . "',
'");
$this->db->query("INSERT INTO "
. DB_PREFIX . "abc
SET
boutiques_id = '" . (int)$this->customer->getId() . "',
firstname = '" . $this->db->escape($data['firstname']) . "',
lastname = '" .$this->db->escape($data['lastname']). "',
}
in abc table it is a primary key and autoincrement whereas in second table it is foreign key.
After an insert query use this $this->db->getLastId(); to get last inserted id of that table, by this u can add this to another table.
The ghetto way assuming there is a unique constraint on boutique_customer_id and ordered_date would be:
INSERT INTO abc (boutiques_id, firstname, lastname)
SELECT boutiques_id, 'John', 'Smith'
FROM xyz
WHERE boutique_customer_id = 123
AND ordered_date = 2015-05-01
Or else you would need to use a transaction/programmatically get and set that foreign key value. Either way I wouldn't try to set the primary key when initially inserting. Finally unrelated to your question, try looking at using an ORM.
I have a comment section and a reply to comment section on my social network. We are having some trouble with manual spammers, and I was going to limit the amount of comments someone could post a day.
Here are the insert queries for comments and reply to comments:
//COMMENTS
$query = "INSERT INTO `CysticAirwaves` (
`FromUserID`,
`ToUserID`,
`comment`,
`status`,
`statusCommentAirwave`,
`date`,
`time`
) VALUES (
'" . $auth->id ."',
'" . $prof->id ."',
'" . mysql_real_escape_string($_POST['ProfileComment']) ."',
'active',
'active',
'" . date("Y-m-d") . "',
'" . date("G:i:s") . "')";
mysql_query($query,$connection);
if($auth->id == $prof->id) {
$just_inserted = mysql_insert_id();
$query = "UPDATE `CysticAirwaves` SET `status` = 'dead' WHERE `FromUserID` = '" . $auth->id . "' AND `ToUserID` = '" . $prof->id . "' AND `id` != '" . $just_inserted . "'";
$request = mysql_query($query,$connection);
}
//REPLIES
$query = "INSERT INTO `CysticAirwaves_replies` (
`AirwaveID`,
`FromUserID`,
`comment`,
`status`,
`date`,
`time`
) VALUES (
'" . mysql_real_escape_string($_POST['comment']) . "',
'" . $auth->id . "',
'" . mysql_real_escape_string($_POST['reply']) . "',
'active',
'" . date("Y-m-d") . "',
'" . date("G:i:s") . "'
)";
mysql_query($query,$connection);
$mailto = array();
/* get the person that wrote the inital comment */
$query = "SELECT `FromUserID` FROM `CysticAirwaves` WHERE `id` = '" . mysql_real_escape_string($_POST['comment']) . "' LIMIT 1";
$request = mysql_query($query,$connection);
$result = mysql_fetch_array($request);
$comment_author = new User($result['FromUserID']);
thanks in advance
You can perform a select to see how many entries are in the table already by that user for the current date:
SELECT COUNT(*)
FROM CysticAirwaves
WHERE userid = $auth->id
AND date = CURDATE()
Then only perform the INSERT if the number is below your threshold. Alternatively, you can place a trigger on the INSERT that does this check with every INSERT and bounces the call as well. ("Best practice" would be to place it in the database as this would be a database-related limitation, but that's your call)
It's been a while since I've done MySQL triggers, but I think think is what you're after:
delimeter |
CREATE TRIGGER reply_threshold BEFORE INSERT ON CysticAirwaves_replies
FOR EACH ROW BEGIN
DECLARE reply_count INT;
SET reply_count = (SELECT COUNT(*) FROM CysticAirwaves_replies WHERE userid = NEW.userid AND `date` = CURDATE());
IF reply_count > 5 THEN
SIGNAL SQLSTATE SET MESSAGE_TEXT = 'Too many replies for today';
END IF;
END;
|
delimeter ;
Essentially, if you go to insert a reply in the table and the threshold has been exceeded, a sql error will be raised stopping the action. You can't "prevent" an insert per-say, but you can raise an exception that makes it fall-through.
You can only limit this by the ip address when you don't have a login system. But the ip can change and this is here the problem.
The best way is to secure the form by a login. That only user can post when they are logged in.
Last technique is to use a captcha like Recaptcha then at most time bots fill out your form and spam to your system.
When you have a login. Then make a table related to your usertable and count the INSERTS. Before you INSERT a new comment check the table if there was a INSERT today.
Before to insert the comment, you check if the user has posted more than 5 comments in the day.
If yes, you don't insert the comment and you display a message.
SELECT COUNT(*) FROM CysticAirwaves_replies WHERE FromUserID = the_user_id AND date = CURDATE()
Besides counting before each insertion, you can store the number of comments made by an user somewhere directly, so you don't have to do the count(*) every time (which can be expensive if an user has lots of comments and the table you have is somewhat big).
Like, on commenting:
SELECT comment_count FROM comment_count_table WHERE user_id = ?
If that value is small enough, you do:
UPDATE comment_count_table SET comment_count = comment_count + 1 WHERE user_id = ?
Be careful with this since you'd need to reset that counter somehow.
At my company we implemented this setting a "last modified field". When we do the SELECT, if the "last modified day" is not today, then we reset the counter.
Another option is to have a cron job that resets the counter for all users once every day, but that is way too expensive.