Mysql query - insert from CSV conditional to comparison between table and CSV - mysql

I'm a rather newbie when it comes to SQL queries and not sure how to approach this:
I have a CSV file that contains 5 columns, 2 of those columns are Value1 and Value2, I need to run over an existing sql table (for this question's purposes I'll call it "target table") and iterate over all rows in target table checking their Value1 column, if that Value1 content equals to the one in the CSV I need to insert Value2 into the Value2 column of that row if the Value1 is not contained in the table, create a new row for it.
Just in case I wasn't clear, here's an example -
assuming the CSV looks like the following:
Name, Age, Location, Height, Weight
David, 12, Macedonia, 1.87, 96
Kim, 15, Denmark, 1.95, 67
I want to go over the existing SQL and work according to name and weight only - if the name David is in the table, insert 96 to its Weight column, if the name Kim is in the table, insert 67 to its Weight column etc...
If the table only contained Kim and not David, then the David row would be created.
I'm assuming the wise way would be to first fill in the gaps of "Value1" that aren't existing in the table and only then run an update on the "Value2" but I might be wrong.
Any help would be much appreciated, thanks!

Theoretically, I think this should work for you.
--Part 1: Clear/Create temporary table and Load CSV into SQL. Credit to mr_eclair for describing this process here
drop table #temp
create table #temp (
tName nvarchar(25),
tAge int,
tLocation nvarchar(25),
tHeight float(3,2), -- alternatively, use cm instead of m and just use int(3)
tWeight int
)
BULK INSERT #temp
FROM 'C:\CSVData\updates.csv'
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = ',', --CSV field delimiter
ROWTERMINATOR = '\n', --Use to shift the control to next row
TABLOCK
)
--Part 2: Setting a Unique Key; as suggested by #Yuri_Lachin
Alter table target
Add Unique (Name) -- Sets Name column as a Unique Key for the table target
--Part 3: Adding rows and Updating values from temp table to permanent table. Credit to MySQL 5.7 Reference Manual 13.2.5.2
Insert into target(Name, Age, Location, Height, Weight)
Select tName, tAge, tLocation, tHeight, tWeight from #temp
On DUPLICATE KEY Update Weight = tWeight
I was going to suggest using a Merge statement like the following, but it looks like MySQL doesn't deal with those.
Merge Into people
using #temp
on target.name = #temp.tname
when matched then Update
set target.weight = #temp.tweight
when not matched then
Insert (target.name, target.age, target.location, target.height, target.weight)
values (#temp.tname, #temp.tage, #temp.tlocation, #temp.theight, #temp.tweight);

Related

I have a SQL query that loops data

I wrote a query to display certain record, but it is displaying extra data, for instance I have only 239 records in my database, but the query is displaying 356 records. Can anyone advice me on what I did wrong, I would really appreciate it. Here is the query:
SELECT DISTINCT
t.branchid,
t.occupancyid,
t.wardnumber,
t.bednumber,
t.admissiondate,
ti.patientname
FROM
bedoccupancydetail t
JOIN
consultationheader ti ON t.occupancyid = ti.occupancyid
WHERE
t.checkedout = '0'
There might not be any problem with your query, just it is how mysql (or any RDBMS) behaves. In your case in the two tables bedoccupancydetail and consultationheader are joined by occupancyid and it seems this columns is not unique and contains duplicate values, for each matching (duplicate) record it adds a row/record after joining.
Let's see the below example which I run at https://www.tutorialspoint.com/execute_sql_online.php:
BEGIN TRANSACTION;
CREATE TABLE NAMES(Id integer PRIMARY KEY, Name text);
INSERT INTO NAMES VALUES(1,'Tom');
INSERT INTO NAMES VALUES(2,'Lucy');
INSERT INTO NAMES VALUES(3,'TOM');
INSERT INTO NAMES VALUES(4,'TOM');
CREATE TABLE ABC(Id integer PRIMARY KEY, Name text, Another text);
INSERT INTO ABC VALUES(1,'Tom', 'A');
INSERT INTO ABC VALUES(2,'Lucy', 'B');
INSERT INTO ABC VALUES(3,'TOM', 'C');
INSERT INTO ABC VALUES(4,'TOM', 'D');
COMMIT;
/* Display all the records from the table */
SELECT ABC.Name, NAMES.Name, ABC.Another
FROM NAMES
JOIN ABC on ABC.Name = NAMES.Name;
As you see each table has 4 rows but the result has 6 rows:
$sqlite3 database.sdb < main.sql
Tom|Tom|A
Lucy|Lucy|B
TOM|TOM|C
TOM|TOM|D
TOM|TOM|C
TOM|TOM|D

SQL how to use a formula to fill a column like in Excel

My database is in MySQL
I have a table, let's say of 4 columns.
I would like to know if it's possible, and how to implement the following: fill the 4th column according to the value of the column 2 and column 3
In Excel I have a formula, let's give an example: if column2 value is set to "grey" and column3 value is set to "car", then column 4 value should be set to "super"
I just say this as an example.
My real formula in Excel looks like this: =IF(K4=4;"Maximal";IF(K4>4;"Maximal";IF(K4=3;"Important";IF(K4>3;"Important";IF(K4=2;"Limited";IF(K4>2;"Limited";IF(K4=1;"Forgettable";IF(K4>1;"Forgettable";"error"))))))))
However I want to do it in SQL.
I was thinking of creating my table until the column 3, set column 4 to NULL or empty, then open a GUI written in Java and maybe there do a piece of code to automatically fill the column 4 according to what is in column 2 and column 3 (these values will be choosable via Choicelist).
But if there is a way to do it directly in SQL, I am interested
Thx a lot in advance for your help.
regards
Yes. you can easily update your NULL-values according to some requirements for the other values in other columns of a particular row with the Update statement
UPDATE <tablename>
SET <column> = 'value'
WHERE <condition>
The only drawback here might be that you have to create an update statement for each of the combinations of your values in column2 and column3. (however, it's not much work for your amount of conditions).
I created an example (demo):
Creating a table in SQL according to your example could look like this,I used a temporary one for the sake of an example:
CREATE GLOBAL TEMPORARY TABLE demoTable (
"Col1" VARCHAR2(50 BYTE) NOT NULL,
"Col2" VARCHAR2(50 BYTE) NOT NULL,
"Col3" VARCHAR2(50 BYTE) NOT NULL,
"Col4" VARCHAR2(50 BYTE) DEFAULT NULL
)
ON COMMIT PRESERVE ROWS
I also inserted some dummy data:
INSERT INTO demoTable VALUES ('Charles', 'grey', 'car', NULL);
INSERT INTO demoTable VALUES ('Alice', 'grey', 'bike', NULL);
INSERT INTO demoTable VALUES ('Bob', 'red', 'car', NULL);
The result:
Now, create the update statements like this, for example:
UPDATE demoTable dt
SET dt."Col4" = 'super'
WHERE dt."Col2" = 'grey' AND dt."Col3" = 'car';
The result
You can try like this;
select * from mytable
COL1 COL2
---- --------------------
0 -
1 -
2 -
3 -
4 -
4 record(s) selected.
update mytable Set Col2 =
Case
When Col1<1 Then 'error'
When Col1=1 Then 'Forget'
When Col1=2 Then 'Limited'
When Col1=3 Then 'Important'
When Col1=4 Then 'Maximal'
End"
select * from mytable"
COL1 COL2
---- --------------------
0 Error
1 Forget
2 Limited
3 Important
4 Maximal
4 record(s) selected.
You can create a sql function, lets say udfGetColumn4Value taking in the column2, column3 as parameters to it and return a value.
Now you can run a select column2, column3, udfGetColumn4Value(column2, column3) from table or a query as desired. Hope this helps.
You were not very precise regarding which DBMS you're using. And also about the exact logic behind using your two columns.
Still here comes a probable SQL-Server solution, where I have taken one statement using CASE WHEN with your example and concatenated your two columns col2 and col3 (you can apply your further logic of here) otherwise:
UPDATE TableName
SET Col4 = CASE WHEN col2 = 'red' AND col3 = 'car' THEN 'super' ELSE col2 + col3 END;
You should replace col2 + col3 with your further logic.
Seems that a simple UPDATE-Query could address your problem:
update things set result = "super" where thing = "car" and color = "grey";
The where-clause does what you desire to do by saying
fill the column 4 according to what is in column 2 and column 3
I created a test table here on turorialspoint, there you can check if it fits your needs.

Duplicating records in the same MySQL table has duplicate entry for key

Is there a way to use a MySQL INSERT similar to the following:
INSERT INTO doc_details SELECT * FROM doc_details WHERE dd_id = 1
This doesn't work because the primary key is being repeated and it can get very long-winded expanding the columns out.
The purpose of this is to duplicate rows in the same table which will get modified later, retrieving the last_insert_id for the new record. So ideas for other ways to do this would be appreciated too.
Thanks.
Simply name the columns you want to duplicate and omit the primary key:
INSERT INTO doc_details (col1, col2, col3)
SELECT col1, col2, col3
FROM doc_details
WHERE dd_id = 1
I'd suggest you to make ID field with AUTO_INCREMENT option, then use NULL values when inserting -
INSERT INTO doc_details(id, column1, column2)
SELECT NULL, column1, column2 FROM doc_details WHERE dd_id = 1;
In this case old ID will be changed with new ones.
You can depend on temporary table to copy from old record and omitting the key field value.
You have to use at least one named column, i.e. the key field name, to omit its repeating values.
See the following example:
CREATE TEMPORARY TABLE tmp SELECT * from doc_details WHERE dd_id = ?;
ALTER TABLE tmp drop pk_field_name_here; -- drop the key field for not repeating
INSERT INTO doc_details SELECT 0, tmp.* FROM tmp;
DROP TABLE tmp;
You can observe that no other filed names are used but the key field name to omit it's value.
You can also refer to my answer to a similar posting at: Mysql: Copy row but with new id.
Thanks for the answers. Really appreciated. Because most answers specify the column, this led to some extra research that said 'wildcards cannot be used in INSERT statements. Select, Modify and insert into the same table
I managed to solve this in my application with a separate SELECT then the INSERT with the columns expanded with a Perl map function:
SELECT * FROM doc_details WHERE dd_id = 1
Then in Perl, with the row as a hash reference in $data:
$data->{'dd_id'} = 0;$columns = join(',', map {$_ .'='. $dbh->quote( $data->{$_} ) } keys %{$cdh} );
Does the trick nicely - it copies the row regardless of changes to the column structure/order as long as the auto_increment column is maintained.
I know it's not a pure SQL solution - although Ravinder provided one that was.
Thanks to all!

How do I check condition while mapping in SSIS?

i want to create one ssis package which takes values from flat file and insert it into database table depending upon there companyname.
for example:
I have table fields:
Date SecurityId SecurityType EntryPrice Price CompanyName
2011-08-31 5033048 Bond 1.05 NULL ABC Corp
now i want to insert Price into this table but i need to match with CompanyName
and in that also in file CompanyName is like ABC so how can i checked that and insert only particular data...
like this i have 20 records in my file with different company names.
I DID LIKE THIS
in lookup i did
and now my problem is i need to check company name from flat file and insert that company price into table but in flat file company name is given like 'AK STL' ans in table it is like 'AK STEEL CORPORATION' so for this i have used column transformation but what expression i write to find match ...same with other company names only 1ft 2-3 charachters are there in flat file please help
Basically, you are looking to "Upsert" your data into the database. Here is a simple look up upsert example. With as few of records in your dataset as you have said, this method will suffice. With larger datasets, you probably want to look into using temp tables and using sql logic similar to this:
--Insert Portion
INSERT INTO FinalTable
( Colums )
SELECT T.TempColumns
FROM TempTable T
WHERE
(
SELECT 'Bam'
FROM FinalTable F
WHERE F.Key(s) = T.Key(s)
) IS NULL
--Update Portion
UPDATE FinalTable
SET NonKeyColumn(s) = T.TempNonKeyColumn(s)
FROM TempTable T
WHERE FinalTable.Key(s) = T.Key(s)
AND CHECKSUM(FinalTable.NonKeyColumn(s)) <> CHECKSUM(T.NonKeyColumn(s))

table with records that can be arranged

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.