mysql insert external data with join - mysql

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
);

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.

mysql copy many records with one change

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

SQL to add a summary row to MySQL result set

If I have a MySQL table such as:
I want to use SQL to calculate the sum of the PositiveResult column and also the NegativeResult column. Normally I could simply do SUM(PositiveResult) in a query.
But what if I wanted to go a step further and place the totals in a row at the bottom of the result set:
Can this be achieved at the data level or is it a presentation layer issue? If it can be done by SQL, how might I do this? I am a bit of an SQL newbie.
Thanks to the respondents. I will now check things with the customer.
Also, can a text column be added so that the value of the last row of data is not shown in the summary row? Like this:
I would also do this in the presentation layer, but you can do it MySQL...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,pos DECIMAL(5,2)
,neg DECIMAL(5,2)
);
INSERT INTO my_table VALUES
(1,0,0),
(2,1,-2.5),
(3,1.6,-1),
(4,1,-2);
SELECT COALESCE(id,'total') my_id,SUM(pos),SUM(neg) FROM my_table GROUP BY id WITH ROLLUP;
+-------+----------+----------+
| my_id | SUM(pos) | SUM(neg) |
+-------+----------+----------+
| 1 | 0.00 | 0.00 |
| 2 | 1.00 | -2.50 |
| 3 | 1.60 | -1.00 |
| 4 | 1.00 | -2.00 |
| total| 3.60 | -5.50 |
+-------+----------+----------+
5 rows in set (0.02 sec)
Here's a hack for the amended problem - it ain't pretty but I think it works...
SELECT COALESCE(id,'') my_id
, SUM(pos)
, SUM(neg)
, COALESCE(string,'') n
FROM my_table
GROUP
BY id
, string
WITH ROLLUP
HAVING n <> '' OR my_id = ''
;
select keyword,sum(positiveResults)+sum(NegativeResults)
from mytable
group by
Keyword
if you need the absolute value put sum(abs(NegativeResults)
This should be handled at least one layer above the SQL query layer.
The initial query can fetch the detail info and then the application layer can calculate the aggregation (summary row). Or, a second db call to fetch the summary directly can be used (although this would be efficient only for cases where the calculation of the summary is very resource-intensive and a second db call is really necessary - most of the time the app layer can do it more efficiently).
The ordering/layout of the results (i.e. the detail rows followed by the "footer" summary row) should be handled at the presentation layer.
I'd recommend doing this at the presentation layer. To do something like this in SQL is also possible.
create table test (
keywordid int,
positiveresult decimal(10,2),
negativeresult decimal(10,2)
);
insert into test values
(1, 0, 0), (2, 1, -2.5), (3, 1.6, -1), (4, 1, -2);
select * from (
select keywordid, positiveresult, negativeresult
from test
union all
select null, sum(positiveresult), sum(negativeresult) from test
) main
order by
case when keywordid is null then 1000000 else keywordid end;
I added ordering using a arbitrarily high number if keywordid is null to make sure the ordered recordset can be pulled easily by the view for displaying.
Result:
+-----------+----------------+----------------+
| keywordid | positiveresult | negativeresult |
+-----------+----------------+----------------+
| 1 | 0.00 | 0.00 |
| 2 | 1.00 | -2.50 |
| 3 | 1.60 | -1.00 |
| 4 | 1.00 | -2.00 |
| NULL | 3.60 | -5.50 |
+-----------+----------------+----------------+

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.

SQL Script to define a table schema using values from another existing table in MYSQL

I have a table in MySQL which is of the following form
ID Team Task
-----------------
1 Team01 Task_01
2 Team02 Task_02
3 Team02 Task_01
The values in the Team and Task column are repetitive.
I need to collect the distinct tasks from the table and use it to create a new table whose schema would be of the form Table (Task_01 varchar(20),Task_02 varchar(20));
Table (Team varchar(15) PRIMARY KEY, Task_01 int, Task_02 int)
Upon creation , I need to populate this new table with frequency of the tasks performed by respective teams.
Need some directions to proceed ahead. Thanks.
[EDIT]
The expected output is another table as shown below
Team | Task_01 | Task_02
Team01 | 1 | 0
Team02 | 1 | 1
Just need help/ideas on how to create the schema(via SQL Scripts only) once all distinct tasks are obtained from the given table.
Given this sample data:
CREATE TABLE Table1
(`ID` int, `Team` varchar(6), `Task` varchar(7))
;
INSERT INTO Table1
(`ID`, `Team`, `Task`)
VALUES
(1, 'Team01', 'Task_01'),
(2, 'Team02', 'Task_02'),
(3, 'Team02', 'Task_01')
;
You would usually just do this
SELECT
Team, Task, COUNT(*)
FROM Table1
GROUP BY Team, Task;
to get this result
| TEAM | TASK | COUNT(*) |
|--------|---------|----------|
| Team01 | Task_01 | 1 |
| Team02 | Task_01 | 1 |
| Team02 | Task_02 | 1 |
which is not in the desired format, but usually formatting is done in application layer. So this is what I recommend. It's easy and performant and has no drawbacks like what do you do when you want to add another task? You would add another column. When a team has nothing to do with this task, it has NULL values in this column. This is bad. Anyway...if you absolutely want to have your output like in the question and not do it on application level, you can do it like this:
SELECT
Team,
COUNT(IF(Task = 'Task_01', 1, NULL)) AS Task_01,
COUNT(IF(Task = 'Task_02', 1, NULL)) AS Task_02
FROM Table1 t
GROUP BY Team
Result:
| TEAM | TASK_01 | TASK_02 |
|--------|---------|---------|
| Team01 | 1 | 0 |
| Team02 | 1 | 1 |