mysql copy many records with one change - mysql

Here is a table Evact:
+--------------+-----------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+-------+
| EvActMas | char(10) | NO | PRI | | |
| EvActSub | char(10) | NO | PRI | | |
| EvActCode | char(10) | NO | PRI | | |
| EvActIncOutg | enum('I','O','B','N') | YES | | NULL | |
| EvActBudAct | enum('B','A','O') | YES | | NULL | |
...other columns ...
and here are some records:
EvActMas EvActSub EvActCode EvActIncOutg EvActBudAct ..other..
Bank-2017 Incoming mth01 I A
Bank-2017 Incoming mth02 I A
Bank-2017 Incoming mth03 I A
Bank-2017 Incoming mth04 I A
Bank-2017 Incoming mth05 I A
Bank-2017 Incoming mth06 I A
I want to add six new records to the table where 'Incoming' is changed to 'Outgoing' and 'I' is changed to 'O'.
I did it the hard way by creating a new table from the old one; updating the new table and then inserting back into Evact:
Create table btemp like Evact;
update btemp set Evact = 'Outgoing', EvActIncOutg = 'O';
insert into Evact select * from btemp;
That worked, but I want to get better at SQL. What I wish for is a way to do this in one step by joining Evact to itself in some way. Does anyone have a suggestion?

If you want to insert a bunch of rows that are part copies of existing rows:
INSERT INTO evact
SELECT evactmas, 'Outgoing', evactcode, 'O', evactbudact, ...other..
FROM evact
You make a Select statement that is the data you want to insert, some columns in the select are the values as-is, other columns are the new values
If you aren't specifying all the columns in the select you'll have to put a list of column names in brackets after the INTO so MySQL knows which columns are to get what data. You can only omit the columns list if your select query selects the same number of columns in the table (in which case the columns selected must be in the same order as the table columns to be inserted into)
If your table has a calculated primary key (auto increment for example) specify the value to insert as 0 or NULL to have MySQL calculate a new value for it, or name all the columns except that one after the INTO and omit it from the select list

Related

Insert data in table using two or more tables

I have two existing table and wants to create third table with help of few columns. The fist two tables are;
Table one: users
|id | name | sid |
| 1 | demo | test1 |
| 2 | anu | test2 |
Table one: insights
| id | description| name |
| 1 | yes | demoone|
| 2 | no | demotwo|
I want to insert data in new table called insight_owner. As per my knowledge, I made below query but that is giving me below error
ERROR 1242 (21000): Subquery returns more than 1 row
The query used is
insert into insight_owner (column_one, column_two, column_three, column_four, column_five) VALUES ('1', '0', NULL, (select u.id from users u where u.sid='test1'), (select i.id from insights i)) ;
Expected output is
| column_one| column_two| column_three| column_four| column_five| column_six |
+----+-----------------+--------------------+---------------+-----------+--------------------+
| 1 | 1 | 1 | NULL | 1 | 1 |
| 2 | 1 | 1 | NULL | 1 | 2 |
column_five = Users id
column_six = Insight id
INSERT...SELECT syntax is what you're looking for (instead of INSERT...VALUES, which is limited to single values per column in each value list). That allows you to select the data directly from the table(s) concerned, using normal SELECT and JOIN syntax. You can also hard-code values which you want to appear on every row, just as you can in a normal SELECT statement. Basically, write the SELECT statement, get it to output what you want. Then stick an INSERT at the start of it and it sends the output to the desired table.
insert into insight_owner (column_one, column_two, column_three, column_four, column_five)
select '1', '0', NULL, (select u.id from users u where u.sid='test1'), i.id
from insights i
You are using
insert into insight_owner (column_one, column_two, column_three, column_four, column_five) VALUES ('1', '0', NULL, (select u.id from users u where u.sid='test1'), (select i.id from insights i));
Which basically inserts one row in your new table.
So, when you add subquery
select i.id from insights i
It will return all rows from insights table an you actually want just one value.
The result you will get is
| id |
| 1 |
| 2 |
And you want
| id |
| 1 |
So, you should be adding conditional that will make sure you are getting only one result as you are doing with first query (where u.sid='test1'), or limit.
I hope this helps.

How to preserve value of one column based on other column values?

I have a requirement wherein I will be getting records which I need to insert into a database (MariaDB 10.3) table wherein for each record I have 2 base values viz. name and amount and one processed value viz. action (imagine this action is something acted upon by user from UI).
+---------+--------+----------------+----------------+
| name | amount | action | created_at |
+---------+--------+----------------+----------------+
| Akshay | 1000 | processed | 2019-08-01 |
+---------+--------+----------------+----------------+
Now, what I want to achieve is when next time I receive a record with name and amount which already exist in the table, then populate action by automatic reference to the previous entry from this same table whose name and amount match.
And if name and amount combination does not exist in the table, then do not populate action.
Desired end result is depicted in structure below:
+---------+--------+----------------+----------------+
| name | amount | action | created_at |
+---------+--------+----------------+----------------+
| Akshay | 1000 | processed | 2019-08-04 |
| Akshay | 1001 | | 2019-08-03 |
| Saanvi | 1000 | | 2019-08-02 |
| Akshay | 1000 | processed | 2019-08-01 |
+---------+--------+----------------+----------------+
Any clues how can I achieve this functionality?
DROP PROCEDURE IF EXISTS db.SP_CREATE_VALUE;
CREATE PROCEDURE db.`SP_CREATE_VALUE`(IN `in_name` VARCHAR(50), IN `in_amount` INT)
BEGIN
DECLARE numAlreadyExists INT(11) DEFAULT 0;
DECLARE strExistingAction VARCHAR(20) DEFAULT "";
SET numAlreadyExists=(SELECT COUNT(*) FROM table_name WHERE name=in_name AND in_amount=in_amount);
IF (numAlreadyExists >0) THEN
SET strExistingAction=(SELECT action FROM table_name WHERE name=in_name AND in_amount=in_amount ORDER BY table_id DESC LIMIT 1);
END IF;
INSERT INTO table_name
name=in_name,amount=in_amount,action=strExistingAction;
END;
And then when you want to create a new record, simply...
CALL SP_CREATE_VALUE('Akshay',1000);
You can use window functions. For instance:
select name, amount,
max(action) over (partition by name, amount order by date) as action,
date
from t;

Overwrite default values from source table using union

In my table, I have few specific keys and few generic keys. Specific data can be got by combining specific key column values on top of generic key column values.
Specific key | Generic key | Col1 | Col2 |
Null | generic key 1 | defaultVal1 | default Val2 |
Specific key1 | generic key 1 | Null | Specific val2 |
In this case my specific record should need to look like,
Specific key1 | generic key 1 | defaultVal1 | Specific val2|
I am trying to achieve this using union statement, but, it is overwriting the Null value for Specific key1 on top of generic defaultVal1.
Hence I would like to get columns overwritten on top of generic records when it is not null. If the columns of specific record is null, then I want to retain the default value.
EDIT:
I tried to provide info as simple as possible, looks like it attracts more downvotes. Here I am trying to explain my schema bit further:
I have 2 tables namely ids_link, core_params
ids_link table contents:
Unique key : specificid
| globalid | specificid | type |
| gid1 | sid1 | type1 |
| gid1 | sid2 | type2 |
| gid2 | sid3 | type1 |
| gid2 | sid4 | type2 |
| gid3 | sid5 | type1 |
core_params table :
Uniquekey : id
| id | coreparam1 | coreparam2 |
| gid1 | defaultVal1 | defaultVal2 |
| sid1 | NULL | sid1Val2 |
| sid2 | sid2val1 | NULL |
| sid3 | sid3val1 | NULL |
In short, more than one specific id share a global id. The global ID will have default values for its corresponding specific ids. The specific id will have specific content that needs to be overwritten on top of its corresponding global id values and returned.
For example)
If I want to return all the specific records for type 1, then my output will be,
| specificid | globalid | type | coreparam1 | coreparam2 |
| sid1 | gid1 | type1 | defaultVal1 | sid1val2 |
| sid3 | gid2 | type1 | sid3val1 | NULL |
If you would have noticed here, for sid1, in core_params table, there is no value for coreparam1. But it is backfilled using the default value from the gid1 record.
For sid3, there is no default record to backfill for coreparam2. Hence it coreparams2 field stays null. I am trying to write a sql query to achieve this. The query I tried :
select specificid,globalid,type,coreparam1,coreparam2 from ids_link left join core_params on ids_link.globalid=coreparams.id where type='type1' union all select specificid,globalid,type,coreparam1,coreparam2 from ids_link left join core_params on ids_link.specificid=coreparams.id where type='type1' LIMIT 10;
But in this, if there are NULL values for records in core_params table for specifickeys like for sid1, coreparam1 column is null, I want that to be backfilled with that of its global value (defaultVal1). Kindly let me know if you need more info.
Maybe this will help to re-create your scenario.
-- Create test table
CREATE TABLE [dbo].[Test](
[Col1] [nvarchar](40) NULL,
[Col2] [nvarchar](40) NULL,
[Col3] [nvarchar](40) NULL,
[Col4] [nvarchar](40) NULL,
)
--Insert test rows
insert into Test Values (Null, 'Generic Key 1', 'dfaultVal1', 'DefaultVal2')
insert into Test Values ('Specific Key 1', 'Generic Key 1', Null, 'Specific Val 2')
You said that u use Specific Key in combination with Generic Key, if this is true you will not reach defaultval1 on Col1, so maybe you need to elaborate better. You could try to work with min/max or criteria like Like and keep selecting subsets till you filter the way you wanted.
select t.col1, t.col2, t.col3, t.col4
from test t,
(select max(col1) good1, max(col2) good2 from test) goodkey
where t.col1 = goodkey.good1 and t.col2 = goodkey.good2
Hope this helps.

mysql insert external data with join

I'm usually pretty resourceful, but I'm stuck on this one. Any help would be appreciated.
Say I've got a table for produce, like this, including counts of sold/in stock for each produce type.
+--------------+--------------+------+-----+
| Field | Type | Null | Key |
+--------------+--------------+------+-----+
| produce_type | varchar(100) | NO | PRI |
| sold_count | int(8) | YES | |
| stock_count | int(8) | YES | |
+--------------+--------------+------+-----+
I'm doing a separate insert using external data for each of the 'stock' and 'sold' counts, with hundreds to thousands of produce_types at a time. I may have data with a given produce_type existing only in the 'stock' or 'sold' data to be inserted, but want all to be present in the table.
So, e.g., doing one insert for sold_count ('potato', 3), ('onion', 5) and one for stock_count ('potato', 8), ('carrots', 6), I'd want to end up with this:
+--------------+------------+-------------+
| produce_type | sold_count | stock_count |
+--------------+------------+-------------+
| potato | 3 | 8 |
| onion | 5 | NULL |
| carrots | NULL | 6 |
+--------------+------------+-------------+
So I'd need to join to existing data upon the second column's insert statement, but all I see here or elsewhere on the web is instructions for joins when inserting from another table.
INSERT IGNORE doesn't do it, as one of the 'potato' columns wouldn't get written to.
INSERT ... ON DUPLICATE KEY UPDATE gets closer but I can't figure out how to set the update field to the value from the dataset I'm inserting.
Do I need to create a temp table for the 2nd insert (+ outer join)? Any structurally simpler way of doing this?
Thanks in advance.
Edit: I think I can probaly use this:
https://stackoverflow.com/a/3466/2540707
Does this work?
insert into produce ( produce_type, sold_count )
select produce_type, sold_count from sold_data
on duplicate key update sold_count = ( select sold_count from sold_data
where produce.produce_type = sold_data.produce_type
);

Postgres Output id of dup rows between 2 tables

I have 2 tables. a master table and a master_staging table. I want to know which rows in the master_staging table also appear in the master table. bellow is the structure of master_staging
--------------+------------------------+-------
id | integer |
firstname | character varying(255) |
lastname | character varying(255) |
email | text |
address | character varying(255) |
country | character varying(255) |
phones | json |
twitters | json |
linkedin | character varying(255) |
urls | json |
source | character varying(255) |
notes | json |
conflict_id | integer | **************************
businessname | character varying(255) |
warnings | json | **************************
has_warning | boolean | **************************
deleted | boolean | **************************
The structure of the master table is the exact same except it does not contain the columns with '**************************' in the third column of the above table.
One solution is to loop over all rows in the master_staging table and query the master table for rows which have the same firstname, lastname, email and from there 'manually' check if all other fields are identical.
I am hoping there is a more elegant solution which will allow me to make one run one sql statement which returns the ids of all rows in master_staging which have duplicates in master. The main problem that is stumping me is I'm not sure how to do deep json equality checks?
You can JOIN two tables on whatever fields make a record unique. For example,
Select M.id
From master M, master_staging MS
Where M.email= MS.email
and M.firstName = MS.firstName
and M.lastName = MS.lastName