I want to do an insert ignore if a column has a specific value.
Say I have a simple table that records changes to a PO over time. Each time the PO is updated, I need to insert a record on this table:
PONum | POLine | submittedBy | ... other columns
100 1 initial-value
100 1 TB
100 2 initial-value
On a PHP page I check for the existence of the PO number in this table. If there isn't one, I insert all the PO lines I find from another table. I set the submittedBy name to "initial-value".
Now later on, say PO Line 3 is added. I want to run through my check again and only insert the new record for line 3 if there isn't already one that has line 3 with a submittedBy of initial-value
INSERT IGNORE INTO PODetailCL (PONum, POLine, submittedBy, ... )
VALUES (100, 1, 'TB', ... ),
(100, 2, 'TB', ...),
(100, 3, 'initial-value', ...)
WHERE submittedBy <> 'initial-value' -- <- something like this but not sure of the syntax
Try using conditional insert multiple rows:
INSERT INTO PODetailCL(PONum, POLine, submittedBy, ... )
SELECT (100, 1, 'TB', ... ), (100, 2, 'TB', ...),(100, 3, 'initial-value', ...)
FROM PODetailCL
WHERE EXISTS (SELECT * FROM PODetailCL WHERE submittedBy <> 'initial-value')
I was able to use a virtual column and then add a unique constraint on that column. After that I can use INSERT IGNORE. (Virtual columns are only available in MySQL version 5.7 or later.)
ALTER TABLE porelcl
ADD COLUMN initialCheck BIT GENERATED ALWAYS
AS (CASE WHEN submittedBy = 'initial-value' THEN b'1' ELSE NULL END)
then after that:
ALTER TABLE porelcl ADD UNIQUE initial_check_index (PONum, POLine, PORelNum, initialCheck)
Now when my code runs through the check again, if tries to insert a duplicate record: SomePO, SomePOLine, SomePORelNum & 'initial-value' the insert won't take place, which is what I need.
Related
So I need to select a value from my table and modify it slightly to use as a value in a new row in the same table when inserting. See below:
INSERT INTO reservationbody(
reservationid,
driverid,
tripnumber,
fromlocation,
tolocation
)
VALUES(
1,
2,
(
SELECT
MAX(tripnumber) + 1
FROM reservationbody
WHERE reservationid = 1
),
'here',
'there'
)
I'm getting the following error:
You can't specify target table 'reservationbody' for update in FROM
clause
I've had a look at other questions and have found plenty of questions on this error but they all seem to be about updates and deletes and all require PK values that already exist. None deal with inserting.
Basically what I'm expecting here is that if SELECT MAX(tripnumber) FROM reservationbody where reservationid = 1 returns a value of 4 then the insert should add a new row with a tripnumber value of 5
Can anyone suggest how I can accomplish this?
Have you tried (I know you can do this in MS SQL):
INSERT INTO reservationbody(reservationid,driverid,tripnumber,
fromlocation,tolocation)
SELECT 1, 2, MAX(tripnumber) + 1, 'here', 'there'
FROM reservationbody
WHERE reservationid = 1
I have this Statement:
INSERT INTO qa_costpriceslog (item_code, invoice_code, item_costprice)
VALUES (1, 2, (SELECT item_costprice FROM qa_items WHERE item_code = 1));
I'm trying to insert a value copy the same data of item_costprice, but show me the error:
Error Code: 1136. Column count doesn't match value count at row 1
How i can solve this?
Use numeric literals with aliases inside a SELECT statement. No () are necessary around the SELECT component.
INSERT INTO qa_costpriceslog (item_code, invoice_code, item_costprice)
SELECT
/* Literal number values with column aliases */
1 AS item_code,
2 AS invoice_code,
item_costprice
FROM qa_items
WHERE item_code = 1;
Note that in context of an INSERT INTO...SELECT, the aliases are not actually necessary and you can just SELECT 1, 2, item_costprice, but in a normal SELECT you'll need the aliases to access the columns returned.
You can just simply e.g.
INSERT INTO modulesToSections (fk_moduleId, fk_sectionId, `order`) VALUES
((SELECT id FROM modules WHERE title="Top bar"),0,-100);
I was disappointed at the "all or nothing" answers. I needed (again) to INSERT some data and SELECT an id from an existing table.
INSERT INTO table1 (id_table2, name) VALUES ((SELECT id FROM table2 LIMIT 1), 'Example');
The sub-select on an INSERT query should use parenthesis in addition to the comma as deliminators.
For those having trouble with using a SELECT within an INSERT I recommend testing your SELECT independently first and ensuring that the correct number of columns match for both queries.
Your insert statement contains too many columns on the left-hand side or not enough columns on the right hand side. The part before the VALUES has 7 columns listed, but the second part after VALUES only has 3 columns returned: 1, 2, then the sub-query only returns 1 column.
EDIT: Well, it did before someone modified the query....
As a sidenote to the good answer of Michael Berkowski:
You can also dynamically add fields (or have them prepared if you're working with php skripts) like so:
INSERT INTO table_a(col1, col2, col3)
SELECT
col1,
col2,
CURRENT_TIMESTAMP()
FROM table_B
WHERE b.col1 = a.col1;
If you need to transfer without adding new data, you can use NULL as a placeholder.
If you have multiple string values you want to add, you can put them into a temporary table and then cross join it with the value you want.
-- Create temp table
CREATE TEMPORARY TABLE NewStrings (
NewString VARCHAR(50)
);
-- Populate temp table
INSERT INTO NewStrings (NewString) VALUES ('Hello'), ('World'), ('Hi');
-- Insert desired rows into permanent table
INSERT INTO PermanentTable (OtherID, NewString)
WITH OtherSelect AS (
SELECT OtherID AS OtherID FROM OtherTable WHERE OtherName = 'Other Name'
)
SELECT os.OtherID, ns.NewString
FROM OtherSelect os, NewStrings ns;
This way, you only have to define the strings in one place, and you only have to do the query in one place. If you used subqueries like I initially did and like Elendurwen and John suggest, you have to type the subquery into every row. But using temporary tables and a CTE in this way, you can write the query only once.
I have this workable query which is inserting proper data into 'selections' table according to some conditions:
INSERT INTO selections (auctionid, selections.order)
VALUES
((SELECT id FROM auctions, game WHERE auctions.BetfairMark = game.BetfairMarketID), 1,
((SELECT id FROM auctions, game WHERE auctions.BetfairMark = game.BetfairMarketID), 2,
((SELECT id FROM auctions, game WHERE auctions.BetfairMark = game.BetfairMarketID), 3
but my problem is how to improve this query to prevent getting same duplicates in table when running this query?
Selections table have 3 columns: id, auctionid, order where id is autoincrement number generated for each new record.
So auctionid and order shouldn't be the same values in record.
Add a UNIQUE INDEX to the (auctionid, order) pair.
ALTER TABLE selections ADD UNIQUE index_name (`auctionid`, `order`)
And when you insert you can use INSERT IGNORE INTO ... so that it ignores duplicates instead of throwing an error. (Useful when you batch insert and the duplicates are expected)
SOLVED!
Created 3 separated queries for each row, working for now!
So with added UNIQUE INDEX to the (auctionid, order) pair have this workable code:
INSERT IGNORE INTO
selections
(
selections.auctionid,
selections.order,
selections.title,
startamount
)
SELECT
auctions.id,
1,
PlayerA,
0.01
FROM
auctions, game
WHERE
auctions.BetfairMark = game.BetfairMarketID
;
INSERT IGNORE INTO
elections
(
selections.auctionid,
selections.order,
selections.title,
startamount
)
SELECT
auctions.id,
2,
PlayerB,
0.01
FROM
auctions, game
WHERE
auctions.BetfairMark = game.BetfairMarketID
;
INSERT IGNORE INTO
selections
(
selections.auctionid,
selections.order,
selections.title,
startamount
)
SELECT
auctions.id,
3,
'third text',
0.01
FROM
auctions, game
WHERE
auctions.BetfairMark = game.BetfairMarketID
;
How can I make a table that has rows with order to one another and can be rearranged:
Example:
Rows:idappearance name
Records
(1,"john"),(2,"mike")
Now, I want to insert "Avi" between them:
not having to worry about rearranging them
(1,"john"),(2,"Avi"),(3,"mike")
This table can have a foreign key in
another table (like departments..).
idappearance is the order of appearance I want
to set, doesn't need to be PK.
It needs to handle about 50K of names so O(n) isn't best solution.
Simple solution would be having reasonable numerical gaps between records. In other words;
(10000,"John"),(20000,"mike")
(10000,"John"),(15000,"Avi"),(20000,"mike")
(10000,"John"),(12500,"tom"),(15000,"avi"),(20000,"mike")
etc..
Gap between records should be determined based on your data domain
You could have a trigger on inserts. I don't use MySQL, but here's the code for sql-server...
Basically, on an insert, the trigger increments the appearanceId of all rows with appearanceId which are equal to or greater than the new appearance id.
CREATE Table OrderedTable
(
id int IDENTITY,
name varchar(50),
appearanceOrder int
)
GO
CREATE TRIGGER dbo.MyTrigger
ON dbo.OrderedTable
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE OrderedTable SET
AppearanceOrder = AppearanceOrder + 1
WHERE AppearanceOrder >= (
SELECT TOP 1 AppearanceOrder
FROM inserted )
AND id NOT IN (
SELECT id
FROM inserted )
END
GO
INSERT INTO OrderedTable VALUES ('Alice', 1)
INSERT INTO OrderedTable VALUES ('Bob', 1)
INSERT INTO OrderedTable VALUES ('Charlie', 1)
INSERT INTO OrderedTable VALUES ('David', 1)
This returns David, Charlie, Bob, Alice as expected.
SELECT *
FROM OrderedTable
ORDER BY AppearanceOrder
Note that I haven't fully tested this implementation. One issue is that it will leave holes in the AppearanceOrder if items are deleted, or the inserts deliberately insert outside the current range. If these matter, they are left as an exercise to the reader ;-)
If appearance order were a double-precision floating point number, you could insert any name between any two adjacent names with a single insert. If you start with a table like this:
create table test (
person_id integer primary key,
person_name varchar(10) not null,
appearance_order double precision not null unique
);
insert into test values (100, 'John', 11);
insert into test values (23, 'Mike', 12);
Insert Avi between them by simply
insert into test values (3, 'Avi', 11.5);
Sort by the column 'appearance_order'.
select * from test order by appearance_order
100 John 11
3 Avi 11.5
23 Mike 12
Insert Eva between John and Avi by
insert into test values (31, 'Eva', 11.25);
select * from test order by appearance_order
100 John 11
31 Eva 11.25
3 Avi 11.5
23 Mike 12
You do need to separate identification from sort order. That means using one column for the id number (and as the target for foreign key references) and another for the appearance order. But, depending on your application, you might not need a unique constraint on appearance_order.
Let's imagine that we have table items...
table: items
item_id INT PRIMARY AUTO_INCREMENT
title VARCHAR(255)
views INT
Let's imagine that it is filled with something like
(1, item-1, 10),
(2, item-2, 10),
(3, item-3, 15)
I want to make multi update view for this items from data taken from this array [item_id] => [views]
'1' => '50',
'2' => '60',
'3' => '70',
'5' => '10'
IMPORTANT! Please note that we have item_id=5 in array, but we don't have item_id=5 in database.
I can use INSERT ... ON DUPLICATE KEY UPDATE, but this way image_id=5 will be inserted into talbe items. How to avoid inserting new key? I just want item_id=5 be skipped because it is not in table.
Of course, before execution I can select existing keys from items table; then compare with keys in array; delete nonexistent keys and perform INSERT ... ON DUPLICATE KEY UPDATE. But maybe there is some more elegant solutions?
Thank you.
You may try to generate a table of literals and update items by joining with the table:
UPDATE items
JOIN (SELECT 1 as item_id, 50 as views
UNION ALL
SELECT 2 as item_id, 60 as views
UNION ALL
SELECT 3 as item_id, 70 as views
UNION ALL
SELECT 5 as item_id, 10 as views
) as updates
USING(item_id)
SET items.views = updates.views;