Create table and insert rows if doesn't exist, else just insert - json

I am able to create table, shred JSON and add data if it does not exist in SQL Server:
DECLARE #json nvarchar(max);
SET #json = N'[{"IplayerName": "Pilipiliz",
"Sname": "kikombe",
"WeightLBs":"60.236"
}]'
IF NOT EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = object_id('Iplayerds'))
BEGIN
SELECT
[IplayerName],
[Sname],
[WeightLBs]
INTO
Iplayerds
FROM
OPENJSON(#json)
WITH (IplayerName NVARCHAR(200),
Sname NVARCHAR(20),
WeightLBs DECIMAL(10,4)
)
END
ELSE
PRINT 'exists'
However, when I try to replace the print statement with insert rows code shown below, it fails
INSERT INTO Iplayerds (IplayerName, Sname, WeightLBs)
VALUES ([IplayerName], [Sname], [WeightLBs]
FROM OPENJSON(#json))
What am I doing wrong?

The INSERT command comes in two flavors:
(1) either you have all your values available, as literals or SQL Server variables - in that case, you can use the INSERT .. VALUES() approach:
INSERT INTO dbo.Iplayerds (IplayerName, Sname, WeightLBs)
VALUES (#IplayerName, #Sname, #WeightLBs)
Note: I would recommend to always explicitly specify the list of column to insert data into - that way, you won't have any nasty surprises if suddenly your table has an extra column, or if your tables has an IDENTITY or computed column. Yes - it's a tiny bit more work - once - but then you have your INSERT statement as solid as it can be and you won't have to constantly fiddle around with it if your table changes.
(2) if you don't have all your values as literals and/or variables, but instead you want to rely on another table, multiple tables, or views, to provide the values, then you can use the INSERT ... SELECT ... approach:
INSERT INTO dbo.Iplayerds (IplayerName, Sname, WeightLBs)
SELECT
[IplayerName], [Sname], [WeightLBs]
FROM
OPENJSON(#json) WITH (IplayerName NVARCHAR(200),
Sname NVARCHAR(20),
WeightLBs DECIMAL(10,4)
)
Here, you must define exactly as many items in the SELECT as your INSERT expects - and those can be columns from the table(s) (or view(s)), or those can be literals or variables. Again: explicitly provide the list of columns to insert into - see above.
You can use one or the other - but you cannot mix the two - you cannot use VALUES(...) and then have a SELECT query in the middle of your list of values - pick one of the two - stick with it.

Related

Mysql Insert if not exist in two column

I looked into MySQL duplicate key but cant figure it out.
I have a table like below:
id series chapter path(can be unique)
I want only insert data and not update. Lets say I have data like below:
seri:Naruto, klasor:567 ==> If both of these exist in table then do not insert.
seri:Naruto, klasor:568 ==> If Naruto exist but 568 does not exist then do insert.
How can I achieve this?
Easiest way would be to define unique index with two columns on that table:
ALTER TABLE yourtable ADD UNIQUE INDEX (seri,klasor);
You may also define two column primary key, which would work just as well.
Then use INSERT IGNORE to only add rows when they will not be duplicates:
INSERT IGNORE INTO yourtable (seri, klasor) VALUES ('Naruto',567);
INSERT IGNORE INTO yourtable (seri, klasor) VALUES ('Naruto',568);
Edit: As per comments, you can't use UNIQUE INDEX which complicates things.
SET #seri='Naruto';
SET #klasor=567;
INSERT INTO yourtable
SELECT seri,klasor FROM (SELECT #seri AS seri, #klasor AS klasor)
WHERE NOT EXISTS (SELECT seri, klasor FROM yourtable WHERE seri=#seri AND klasor=#klasor);
You may use the above query with two local variables or convert it to single statement by replacing the local variables with actual values.
Better way would be to use stored procedure:
CREATE PROCEDURE yourinsert (vseri VARCHAR(8), vklasor INT)
BEGIN
DECLARE i INT;
SELECT COUNT(*) INTO i FROM yourtable WHERE seri=vseri AND klasor=vklasor;
IF i=0 THEN
INSERT INTO yourtable (seri,klasor) VALUES (vseri, vklasor);
END IF;
END;
This would allow you to perform the INSERT using:
CALL yourinsert('Naruto',567);
INSERT INTO table_name (seri, klasor) VALUES ('Naruto',567)
WHERE NOT EXISTS( SELECT seri,klasor FROM table_name WEHERE seri='Naruto' AND klasor=567
)
Hope this helps..

Procedure fails but works without variables

At the moment I'm working with databses and I got a strange error wirth procedures. I have create one which gets three parameters of the type VARCHAR: title, interpret, album. Here is the code:
BEGIN
INSERT IGNORE INTO Interpret (Name) VALUES (interpret);
SET #idInterpret := (SELECT id FROM Interpret WHERE Name = interpret);
INSERT IGNORE INTO Album (Name, Interpret) VALUES (album, #idInterpret);
SET #idAlbum := (SELECT id FROM Album WHERE Name = album AND Interpret = #idInterpret);
INSERT INTO Lied (Name, Album, Interpret) VALUES (title, #idAlbum, #idInterpret);
END
If I start this procedure I get an error which says that the album field can not be null (which is right) but it shouldn't be null because I read the value from the table above. If I call exact the same lines of SQL with real data (not as procedure with varaibles) all works great. Do you have any ideas why this happens?
Avoid naming variables and parameter as columns of your tables.
In your query:
SET #`idAlbum` := (SELECT `id`
FROM `Album`
WHERE `Name` = `album` AND `Interpret` = #`idInterpret`);
Interpret, are you referring to the parameter or column of the table?. We know that is column, MySQL interprets is the parameter.
SQL Fiddle demo
See:
13.6.4.2 Local Variable Scope and Resolution
Name Conflicts within Stored Routines in D.1 Restrictions on Stored Programs
Follow the comment #Bernd-Buffen using local varibales for this case.
To use vars in a Stored Procedure you must DECLARE it and then you
can use them without quotes. I have changed your Query, but not tested.
BEGIN
DECLARE idInterpret DEFAULT ='';
DECLARE idAlbum DEFAULT ='';
INSERT IGNORE INTO Interpret (Name) VALUES (interpret);
SELECT id IN TO idInterpret FROM Interpret WHERE Name = interpret;
INSERT IGNORE INTO Album (Name, Interpret) VALUES (album, idInterpret);
SELECT id INTO idAlbum FROM Album WHERE Name = album AND Interpret = idInterpret;
INSERT INTO Lied (Name, Album, Interpret) VALUES (title, idAlbum, idInterpret);
END

Insert only when auto-increment id is not equal 6(for example)?

I have a table with 3 fields: Id(PK,AI), Name(varchar(36)), LName(varchar(36)).
I have to insert name and last name, Id inserts automatically because of it's constraints,
Is There a way to Jump id auto increment value when it reaches 6?
for instance do this 7 times:
Insert Into table(Name, LName) Values ('name1', 'lname1') "And jump id to 7 if it is going to be 6"
It may sound stupid to do this but I have the doubt.
Also Jump and do not record id 6.
record only, 1-5, 7,8,9 and so on
What I want to achieve starts from a Union:
Select * From TableNames
Union All
Select * From TableNames_general
In the TableNames_general I assign it's first value so that when the user sees the table for the first time it will be displayed the record I inserted.
The problem comes when the user inserts a new record, if the Id of the inserted record is the same as the one I have inserted it will be duplicated, that is why I want to achieve when the users inserts one record and if the last insert id already exists just jump that record. this is because I must have different ids due to its relationship among child tables.
Identity column generate values for you, And its best left this way, You have the ability to insert specific values in Identity column but its best left alone and let it generate values for you.
Imagine you have inserted a value explicitly in an identity column and then later on Identity column generates the same value for you, you will end up with duplicates.
If you want to have your input in that column then why bother with identity column anyway ??
Well this is not the best practice but you can jump to a specific number by doing as follows:
MS SQL SERVER 2005 and Later
-- Create test table
CREATE TABLE ID_TEST(ID INT IDENTITY(1,1), VALUE INT)
GO
-- Insert values
INSERT INTO ID_TEST (VALUE) VALUES
(1),(2),(3)
GO
-- Set idnentity insert on to insert values explicitly in identity column
SET IDENTITY_INSERT ID_TEST ON;
INSERT INTO ID_TEST (ID, VALUE) VALUES
(6, 6),(8,8),(9,9)
GO
-- Set identity insert off
SET IDENTITY_INSERT ID_TEST OFF;
GO
-- 1st reseed the value of identity column to any smallest value in your table
-- below I reseeded it to 0
DBCC CHECKIDENT ('ID_TEST', RESEED, 0);
-- execute the same commad without any seed value it will reset it to the
-- next highest idnetity value
DBCC CHECKIDENT ('ID_TEST', RESEED);
GO
-- final insert
INSERT INTO ID_TEST (VALUE) VALUES
(10)
GO
-- now select data from table and see the gap
SELECT * FROM ID_TEST
If you query the database to get the last inserted ID, then you can check if you need to increment it, by using a parameter in the query to set the correct ID.
If you use MSSQL, you can do the following:
Before you insert check for the current ID, if it's 5, then do the following:
Set IDENTITY_INSERT to ON
Insert your data with ID = 7
Set IDENTITY_INSERT to OFF
Also you might get away with the following scenario:
check for current ID
if it's 5, run DBCC CHECKIDENT (Table, reseed, 6), it will reseed the table and in this case your next identity will be 7
If you're checking for current identity just after INSERT, you can use SELECT ##IDENTITY or SELECT SCOPE_IDENTITY() for better results (as rcdmk pointed out in comments)
Otherwise you can just use select: SELECT MAX(Id) FROM Table
There's no direct way to influence the AUTO_INCREMENT to "skip" a particular value, or values on a particular condition.
I think you'd have to handle this in an AFTER INSERT trigger. An AFTER INSERT trigger can't update the values of the row that was just inserted, and I don't think it can make any modifications to the table affected by the statement that fired the trigger.
A BEFORE INSERT trigger won't work either, because the value assigned to an AUTO_INCREMENT column is not available in a BEFORE INSERT trigger.
I don't believe there's a way to get SQL Server IDENTITY to "skip" a particular value either.
UPDATE
If you need "unique" id values between two tables, there's a rather ugly workaround with MySQL: roll your own auto_increment behavior using triggers and a separate table. Rather than defining your tables with AUTO_INCREMENT attribute, use a BEFORE INSERT trigger to obtain a value.
If an id value is supplied, and it's larger than the current maximum value from the auto_increment column in the dummy auto_increment_seq table, we'd need to either update that row, or insert a new one.
As a rough outline:
CREATE TABLE auto_increment_seq
(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=MyISAM;
DELIMITER $$
CREATE TRIGGER TableNames_bi
BEFORE INSERT ON TableNames
FOR EACH ROW
BEGIN
DECLARE li_new_id INT UNSIGNED;
IF ( NEW.id = 0 OR NEW.id IS NULL ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NULL);
SELECT LAST_INSERT_ID() INTO li_new_id;
SET NEW.id = li_new_id;
ELSE
SELECT MAX(id) INTO li_max_seq FROM auto_increment_seq;
IF ( NEW.id > li_max_seq ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NEW.id);
END IF;
END IF;
END$$
CREATE TRIGGER TableNames_ai
AFTER INSERT ON TableNames
FOR EACH ROW BEGIN
DECLARE li_max_seq INT UNSIGNED;
SELECT MAX(id) INTO li_max_seq FROM auto_increment_seq;
IF ( NEW.id > li_max_seq ) THEN
INSERT INTO auto_increment_seq (id) VALUES (NEW.id);
END IF;
END;
DELIMITER ;
The id column in the table could be defined something like this:
TableNames
( id INT UNSIGNED NOT NULL DEFAULT 0 PRIMARY KEY
COMMENT 'populated from auto_increment_seq.id'
, ...
You could create an identical trigger for the other table as well, so the two tables are effectively sharing the same auto_increment sequence. (With less efficiency and concurrency than an Oracle SEQUENCE object would provide.)
IMPORTANT NOTES
This doesn't really insure that the id values between the tables are actually kept unique. That would really require a query of the other table to see if the id value exists or not; and if running with InnoDB engine, in the context of some transaction isolation levels, we might be querying a stale (as in, consistent from the point in time at the start of the transaction) version of the other table.
And absent some additional (concurrency killing) locking, the approach outline above is subject to a small window of opportunity for a "race" condition with concurrent inserts... the SELECT MAX() from the dummy seq table, followed by the INSERT, allows a small window for another transaction to also run a SELECT MAX(), and return the same value. The best we can hope for (I think) is for an error to be thrown due to a duplicate key exception.
This approach requires the dummy "seq" table to use the MyISAM engine, so we can get an Oracle-like AUTONOMOUS TRANSACTION behavior; if inserts to the real tables are performed in the context of a REPEATABLE READ or SERIALIZABLE transaction isolation level, reads of the MAX(id) from the seq table would be consistent from the snapshot at the beginning of the transaction, we wouldn't get the newly inserted (or updated) values.
We'd also really need to consider the edge case of an UPDATE of row changing the id value; to handle that case, we'd need BEFORE/AFTER UPDATE triggers as well.

MySql Basic table creation/handing

I'm trying to create a simple table where I insert field and I do some checks in MySql. I've used Microsoft SQL relatively easy. Instead, MySql give evrrytime query errors without even specifying what's going on. Poor MySql software design apart, here's what I'm trying to do:
1 table with 4 fields with an autoincremental autogenerated number to det an ID as primary key
CREATE TABLE `my_db`.`Patients_table` (
`ID_Patient` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`Patient_name` VARCHAR( 200 ) NOT NULL ,
`Recovery_Date` DATETIME NOT NULL ,
`Recovery_count` INT NOT NULL
) ENGINE = MYISAM
a simple stored procedure to insert such fields and check if something exist before inserting:
CREATE PROCEDURE nameInsert(IN nome, IN data)
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
IF (EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0) THEN
INSERT INTO (Patients_table (Recovery_count)
ELSE
SET Recovery_count = select Recovery_count+1 from Patients_table
END
this seems wrong on many levels and MySQL useless syntax checker does not help.
How can I do this? Thanks.
There seems to be a lot wrong with this block of code. (No offense intended!)
First, Procedures need to be wrapped with BEGIN and END:
CREATE PROCEDURE nameInsert(IN nome, IN data)
BEGIN
...[actually do stuff here]
END
Second, since your table is declared with all fields as NOT NULL, you must insert all fields with an INSERT statement (this includes the Recovery_Date column, and excludes the AUTO_INCREMENT column). You can add DEFAULT CURRENT_TIMESTAMP to the date column if you want it to be set automatically.
INSERT INTO Patients_table (Patient_name,Recovery_Date) values (nome,data)
Third, what exactly is your IF predicate doing?
EXISTS (SELECT Recovery_count FROM Tabella_nomi) = 0
If you want to check if a row exists, don't put the = 0 at the end. Also, Tabella_nomi isn't declared anywhere in that procedure. Also, your SELECT statement should have a WHERE clause, since I'm assuming you want to select a specific row (this is going to select a result set of all recovery_counts).
Fourth, the second INSERT statement seems a little messy. It should look more like the first INSERT, and keep the point I made above in mind.
INSERT INTO (Patients_table (Recovery_count)
Fifth, the ELSE statement
SET Recovery_count = select Recovery_count+1 from Patients_table
Has some problems too. SET is meant for setting variables, not values in rows. I'm not 100% sure what your intent is from this statement, but it looks like you meant to increment the Recovery_count column of a certain row if it already exists. In which case, you meant to do something like this:
UPDATE Patients_table SET Recovery_count = Recovery_count+1 WHERE <conditional predicate>
Where the conditional predicate is something like this:
Patients_name = nome
Try these things, and look at the errors it gives you when you try to execute the CREATE STATEMENT. I bet they're more useful then you think!

putting 2 values in a variable for conditioning purpose SQL Server

if(OBJECT_ID('EgTb')is not null)drop table [EgTB];
GO
create table [EgTB](Name varchar(50),country varchar(50))
insert into EgTB values('aaa','sg');
insert into EgTB values('bbb','uk');
insert into EgTB values('ccc','us');
insert into EgTB values('ddd','au');
GO
select * from EgTB;
declare #cou varchar(50), #firstCou varchar(50), #secondCou varchar(50);
set #firstCou='sg';
set #secondCou='uk';
if(#cou is null)
begin
set #cou=#firstCou+','+#secondCou;
end
select * from EgTB where (#cou is null or country in (#cou));
GO
if(OBJECT_ID('EgTb')is not null)drop table [EgTB];
GO
The above code is the one I stimulated.
there is a variable #cou.
That variable is the one using in where.
what I want is.. if #cou is null, I want to use #firstCou and #secondCou.
Instead of doing where country in (#firstCou,#secondCou)
I want where country in (#cou) with those 2 variable values in #cou.
cos the real thing is not 2 extra variable.. I will retreieve several records and I won't know the fixed record.
That's why I want to use IN()
But my problem is when I add those 2 values into #cou, the #cou value becomes 'sg,uk' instead of 'sg','uk'.
it is expected but how can i make it so that it becomes 'sg','uk'
You can not replace the parameters for IN with a string literal. In takes a list of expressions or a sub-query as an argument.
You could use a table variable instead with one column and one row for each country.
declare #cou table(country varchar(50))
Insert the countries you look for to the table variable and use a query that looks like this.
select *
from EgTB
where country in (select country from #cou)