Join/merge two tables, improvise/make up "missing" entries - mysql

I have two tables, tbl_foo and tbl_bar, and I want to join these tables with tbl_foo.foo_id = tbl_bar.foo_id in the on-clause. However, for each tbl_bar.baz_id there should be one row for each tbl_foo.foo_id (even if no such entry in tbl_bar exists). How do I write such query?
There's more info on the schema and my desired result below.
Edit: Each row must have a foo_id and baz_id.
Edit 2: Added tbl_baz below.
Desired result
+--------+--------+--------+------------+
| bar_id | baz_id | foo_id | some_field |
+--------+--------+--------+------------+
| 1 | 101 | 1 | foo |
| 2 | 101 | 2 | bar |
| 3 | 101 | 3 | baz |
| NULL | 101 | 4 | bin |
| 4 | 102 | 1 | foo |
| NULL | 102 | 2 | bar |
| 5 | 102 | 3 | baz |
| NULL | 102 | 4 | bin |
+--------+--------+--------+------------+
Table: tbl_foo
+--------+------------+
| foo_id | some_field |
+--------+------------+
| 1 | foo |
| 2 | bar |
| 3 | baz |
| 4 | bin |
+--------+------------+
Table: tbl_bar
+--------+--------+--------+
| bar_id | baz_id | foo_id |
+--------+--------+--------+
| 1 | 101 | 1 |
| 2 | 101 | 2 |
| 3 | 101 | 3 |
| 4 | 102 | 1 |
| 5 | 102 | 3 |
+--------+--------+--------+
Table: tbl_baz
+--------+
| baz_id |
+--------+
| 101 |
| 102 |
+--------+
SQL Schema
CREATE TABLE tbl_foo (
foo_id INT,
some_field VARCHAR(255),
PRIMARY KEY (foo_id)
);
INSERT INTO tbl_foo VALUES
(1, 'foo'),
(2, 'bar'),
(3, 'baz'),
(4, 'bin');
CREATE TABLE tbl_bar (
bar_id INT,
baz_id INT,
foo_id INT,
PRIMARY KEY (bar_id, baz_id),
FOREIGN KEY (baz_id) REFERENCES tbl_baz (baz_id),
FOREIGN KEY (foo_id) REFERENCES tbl_foo (foo_id)
);
INSERT INTO tbl_bar VALUES
(1, 101, 1),
(2, 101, 2),
(3, 101, 3),
(4, 102, 1),
(5, 102, 3);
CREATE TABLE tbl_baz (
baz_id INT,
PRIMARY KEY (baz_id)
);
INSERT INTO tbl_baz VALUES
(101),
(102);

Like mwigdalh said, there's no way to achieve that output with the given tables. If there was another baz table, there would be a way. The problem is that the highlighted records below are essentially pulled from thin air, and meaningless. You could just as easily put "meh" in each one, and the output would make as much sense.
+--------+--------+--------+------------+
| bar_id | baz_id | foo_id | some_field |
+--------+--------+--------+------------+
| 1 | 101 | 1 | foo |
| 2 | 101 | 2 | bar |
| 3 | 101 | 3 | baz |
| NULL | *101*| 4 | bin |
| 4 | 102 | 1 | foo |
| NULL | *102*| 2 | bar |
| 5 | 102 | 3 | baz |
| NULL | *102*| 4 | bin |
+--------+--------+--------+------------+
If you provide some context in a closer-to-real-world example, it might be found that there's a different output altogether that achieves your desired result.

You may be looking for a query like this:
UPDATE
Based upon new tbl_baz:
select y.bar_id, x.baz_id, x.foo_id, x.some_field
from (
select a.foo_id, a.some_field, b.baz_id
-- Cross foo_id with all baz_id
from tbl_foo as a, tbl_baz as b
) as x
-- Get the bar_id where it exists for each foo_id/baz_id combo
left join tbl_bar as y on x.foo_id = y.foo_id
and x.baz_id = y.baz_id
order by x.baz_id, x.foo_id
This is based on the assumption that you want to see each foo_id for each baz_id regardless of what is in your many-to-many table.
EXAMPLE of why you may not want this, or may want to update your many-to-many table instead:
If we replace "foo" and "baz" with "person" and "car", this query is essentially saying that every person owns every car. This may be the case, but it is certainly not represented in the "ownership" many-to-many table (bar).

Vague, you can't get there from here. You're asking for a result that specifies a baz_id for rows where there is no corresponding row from tbl_bar. There is simply no way to construct the missing data in this case.
Either your schema is not correct or you need some custom default logic for cases where you can't find a row in tbl_bar.

Related

Is there a way in MySQL to update a view with data from a table that is not covered by the view?

I have two tables, test1 and test2:
test1
| id | name |
| -- | ----- |
| 1 | Steve |
| 2 | Phil |
| 3 | Tony |
test2
| id | name |
| -- | ----- |
| 4 | Peter |
| 5 | Perry |
I have created a view from test1 using the code
CREATE VIEW test_view AS SELECT * from test1;
I wish to update this test_view with data from test2.
What I want is as shown below
| id | name |
| -- | ----- |
| 1 | Steve |
| 2 | Phil |
| 3 | Tony |
| 4 | Peter |
| 5 | Perry |
I am hoping to find a way to do this without joining the two tables.
You can use a union.
create table a (id int, name varchar(255));
create table b (id int, name varchar(255));
insert into a values (1, "c"), (2, "d");
insert into a values (5, "zz"), (6, "ff");
insert into b values (1, "a"), (2, "b");
create view c as select * from a union select * from b;
select * from c;
+------+------+
| id | name |
+------+------+
| 1 | a |
| 2 | b |
| 1 | c |
| 2 | d |
| 5 | zz |
| 6 | ff |
+------+------+
As per Solarflare -
A view is just the result of a query. The "content" of a view changes
when you change the underlying data (e.g. table test1) - and it
doesn't change if you don't change the underlying data
So, when I perform, test1 = test1.append(test2), the test_view that I have created from the underlying test1 dataframe gets updated automatically.

Mysql - joining labels and values table and including not referenced labels as null

The following problem has been bugging me for some time now. I have two tables in my database.
First I have a table holding labels, referenced values.
| option_id | option_name |
|-----------|-------------|
| 1 | Blue |
| 2 | Red |
| 3 | Black |
Then I have the second table with the actual values
| record_id | option_id | first_name | profession |
|-----------|-----------|------------|------------|
| 1 | 2 | James | Clerk |
| 2 | 2 | Ethan | Clerk |
| 3 | 1 | Marian | Nurse |
| 4 | 3 | Bob | Nurse |
| 5 | 3 | Paul | Nurse |
How can I join these two tables in MySQL so I get all the options listed for each of the professions even when there is no reference value so it will show up as NULL?
So the table would look something like:
| profession | option_name | first_name |
|------------|-------------|------------|
| Clerk | Blue | NULL |
| Clerk | Red | James |
| Clerk | Red | Ethan |
| Clerk | Black | NULL |
| Nurse | Blue | Marian |
| Nurse | Red | NULL |
| Nurse | Black | Bob |
| Nurse | Black | Paul |
Any help would be appreciated. Here is the sample database
CREATE TABLE options (
option_id INT,
option_name TEXT
);
INSERT INTO options VALUES (1, 'Blue');
INSERT INTO options VALUES (2, 'Red');
INSERT INTO options VALUES (3, 'Black');
CREATE TABLE records (
record_id INT,
option_id INT,
first_name TEXT,
profession TEXT
);
INSERT INTO records VALUES (1, 2, 'James','Clerk');
INSERT INTO records VALUES (2, 2, 'Ethen','Clerk');
INSERT INTO records VALUES (3, 1, 'Marian','Nurse');
INSERT INTO records VALUES (4, 3, 'Bob', 'Nurse');
INSERT INTO records VALUES (5, 3, 'Paul', 'Nurse');
You can enumerate the professions from the records table, cross join table options to generate all possible combinations, then bring the records table with a left join:
select p.profession, o.option_name, r.first_name
from (select distinct profession from records) p
cross join options o
left join records r
on r.option_id = o.option_id
and r.profession = p.profession
order by p.profession, o.option_name

How to sum the values of column on duplicate key?

I have a table like this:
// mytable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 10 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 5 | jack | 1 | 5 |
| 6 | jack | 1 | 10 |
| 7 | bert | 4 | 2 |
| 8 | peter | 2 | 10 |
| 9 | bert | 4 | 5 |
+----+--------+-------+-------+
Now I want to sum the numbers of value where both name and key are identical. So, I want this output:
// mynewtable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 25 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
+----+--------+-------+-------+
Is it possible to I do that?
Edit: How can I do that for insert?
// mytable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 25 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
+----+--------+-------+-------+
Inserting these rows:
+----+--------+-------+-------+
| 10 | jack | 1 | 5 |
+----+--------+-------+-------+
+----+--------+-------+-------+
| 11 | bert | 1 | 2 |
+----+--------+-------+-------+
What I want: (output)
// mynewtable
+----+--------+-------+-------+
| id | name | key | value |
+----+--------+-------+-------+
| 1 | jack | 1 | 30 |
| 2 | peter | 1 | 5 |
| 3 | jack | 2 | 5 |
| 4 | ali | 1 | 2 |
| 7 | bert | 4 | 7 |
| 8 | peter | 2 | 10 |
| 11 | bert | 1 | 2 |
+----+--------+-------+-------+
You have to group by more columns.
select name, key, sum(value) from mytable group by name, key;
Group by name, key
select name, key, sum(value) as value
from mytable group by name,key
check this
CREATE TABLE #testing_123
([id] int, [name] varchar(5), [key] int, [value] int)
;
INSERT INTO #testing_123
([id], [name], [key], [value])
VALUES
(1, 'jack', 1, 10),
(2, 'peter', 1, 5),
(3, 'jack', 2, 5),
(4, 'ali', 1, 2),
(5, 'jack', 1, 5),
(6, 'jack', 1, 10),
(7, 'bert', 4, 2),
(8, 'peter', 2, 10),
(9, 'bert', 4, 5)
;
query used was
select min(id) id ,name,[key],sum(value) value from #testing_123 group by name,[key] order by 1
output after insert
For the first part (to get the id column in the way requested), you could work along:
INSERT INTO mynewtable
(id, name, `key`, `value`)
SELECT
MIN(id), name, `key`, SUM(`value`)
FROM mytable
GROUP BY name, `key`
;
Now, provided mynewtable is defined with a unique index on name and key like
CREATE TABLE mynewtable
(id INT, name VARCHAR(5), `key` INT, `value` INT, UNIQUE (name, `key`));
you'd get the requested result with
INSERT INTO mynewtable
(id, name, `key`, `value`)
VALUES
(10, 'jack', 1, 5),
(11, 'bert', 1, 2)
ON DUPLICATE KEY UPDATE `value` = `value` + VALUES(`value`)
;
Beware:
It requires the unique index on name and key to work.
It might not work correctly, if there are other unique indexes and/or a primary key on the same table as well.
NB:
Please try to avoid the use of reserved words such as value and key for, e.g., column names.

How to query multiple tables using a single query?

I want my tables to output something like this
---------------------------------------------------------------------------------------------
| date | location | time | delegate 1 | delegate 2 |
|--------------------------------------------------------------------------------------------
| 2015-12-07 | Table 1 | 9:00 | first_name_4 last_name_4 | first_name_5 last_name_5 |
|--------------------------------------------------------------------------------------------
| | 9:30 | first_name_4 last_name_4 | first_name_6 last_name_6 |
|--------------------------------------------------------------------------------------------
| | 9:30 | first_name_3 last_name_3 | first_name_7 last_name_7 |
|--------------------------------------------------------------------------------------------
| | 9:00 | first_name_3 last_name_3 | first_name_7 last_name_7 |
|--------------------------------------------------------------------------------------------
Here are the tables on my db
meetings table
-------------------------------------------------------------------------------------------------
| id | date_id | time_id | location_id | delegate_id_1 | delegate_id_2 | status |
|------------------------------------------------------------------------------------------------
| 1 | 1 | 1 | 1 | 4 | 5 | A |
|------------------------------------------------------------------------------------------------
| 2 | 1 | 2 | 1 | 4 | 6 | A |
|------------------------------------------------------------------------------------------------
| 3 | 1 | 1 | 1 | 2 | 6 | P |
|------------------------------------------------------------------------------------------------
| 4 | 1 | 2 | 1 | 1 | 3 | A |
|------------------------------------------------------------------------------------------------
| 5 | 1 | 1 | 1 | 1 | 3 | A |
|------------------------------------------------------------------------------------------------
users table
-----------------------------------------
| id | first_name | last_name |
|----------------------------------------
| 1 | first_name_1 | last_name_1 |
|----------------------------------------
| 2 | first_name_2 | last_name_2 |
|----------------------------------------
| 3 | first_name_3 | last_name_3 |
|----------------------------------------
| 4 | first_name_4 | last_name_4 |
|----------------------------------------
| 5 | first_name_5 | last_name_5 |
|----------------------------------------
| 6 | first_name_6 | last_name_6 |
|----------------------------------------
locations table
-----------------------------
| id | location_name |
|----------------------------
| 1 | Table 1 |
|----------------------------
time table
-------------------------
| id | meeting_time |
|------------------------
| 1 | 9:00:00 |
|------------------------
| 1 | 9:30:00 |
|------------------------
dates table
-------------------------
| id | meeting_date |
|------------------------
| 1 | 2015-12-07 |
|------------------------
| 2 | 2015-12-08 |
|------------------------
| 3 | 2015-12-09 |
|------------------------
My initial query goes like this
-- $query_date
SELECT meeting_date
FROM dates
WHERE meeting_date = '2015-12-07'
-- $query_location
SELECT location_name.location
from location
LEFT JOIN meetings
ON meetings.location_id=location.id
LEFT JOIN date
ON meetings.date_id=date.id
WHERE meeting_date.dates = '2015-12-07'
Now, here's the part where I got it wrong.
-- $query_final
SELECT meeting_time.time, delegate1.first_name AS first_name_1,
delegate1.last_name AS last_name_1, delegate2.first_name AS first_name_2,
delegate2.last_name AS last_name_2
FROM meetings
INNER JOIN users delegate1
ON meetings.delegate_id_1=users.id
LEFT JOIN users delegate2
ON meetings.delegate_id_2=users.id
WHERE meetings.status='A'
The results on my last query give me unexpected results since the results show more entries than my meetings table.
I know the queries I made are costly but I don't know how to make a more optimized query. I don't even know if it's possible to get the results into a single query only. Any help well do. Thanks.
You can bring back everything with a single query with the right JOIN.
Be Careful, when you use column name on SQL, the syntax is TABLE.COLUMN_NAME, it seem you mistake on the order quit often...
I changed some table name as you sometime use an s at the end and sometime no.
As time and date are SQL keyword, it's better with s everywhere
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE meetings (`id` int, `date_id` int, `time_id` int, `location_id` int, `delegate_id_1` int, `delegate_id_2` int, `status` varchar(1));
INSERT INTO meetings (`id`, `date_id`, `time_id`, `location_id`, `delegate_id_1`, `delegate_id_2`, `status`)
VALUES (1, 1, 1, 1, 4, 5, 'A'),
(2, 1, 2, 1, 4, 6, 'A'),
(3, 1, 1, 1, 2, 6, 'P'),
(4, 1, 2, 1, 1, 3, 'A'),
(5, 1, 1, 1, 1, 3, 'A');
CREATE TABLE users (`id` int, `first_name` varchar(12), `last_name` varchar(11));
INSERT INTO users (`id`, `first_name`, `last_name`)
VALUES (1, 'first_name_1', 'last_name_1'),
(2, 'first_name_2', 'last_name_2'),
(3, 'first_name_3', 'last_name_3'),
(4, 'first_name_4', 'last_name_4'),
(5, 'first_name_5', 'last_name_5'),
(6, 'first_name_6', 'last_name_6');
CREATE TABLE locations (`id` int, `location_name` varchar(7));
INSERT INTO locations (`id`, `location_name`)
VALUES (1, 'Table 1');
CREATE TABLE times (`id` int, `meeting_time` varchar(7));
INSERT INTO times (`id`, `meeting_time`)
VALUES (1, '9:00:00'),
(2, '9:30:00') ;
CREATE TABLE dates (`id` int, `meeting_date` varchar(10)) ;
INSERT INTO dates (`id`, `meeting_date`)
VALUES (1, '2015-12-07'),
(2, '2015-12-08'),
(3, '2015-12-09') ;
Query 1:
-- $query_final
SELECT locations.location_name,
`times`.meeting_time,
delegate1.first_name AS first_name_1,
delegate1.last_name AS last_name_1,
delegate2.first_name AS first_name_2,
delegate2.last_name AS last_name_2
FROM meetings
LEFT JOIN locations
ON meetings.location_id=locations.id
LEFT JOIN dates
ON meetings.date_id=`dates`.id
LEFT JOIN times
ON meetings.time_id=`times`.id
INNER JOIN users delegate1
ON meetings.delegate_id_1 = delegate1.id
LEFT JOIN users delegate2
ON meetings.delegate_id_2 = delegate2.id
WHERE
meetings.status = 'A'
AND dates.meeting_date = '2015-12-07'
Results:
| location_name | meeting_time | first_name | last_name | first_name | last_name |
|---------------|--------------|--------------|-------------|--------------|-------------|
| Table 1 | 9:00:00 | first_name_1 | last_name_1 | first_name_3 | last_name_3 |
| Table 1 | 9:30:00 | first_name_1 | last_name_1 | first_name_3 | last_name_3 |
| Table 1 | 9:00:00 | first_name_4 | last_name_4 | first_name_5 | last_name_5 |
| Table 1 | 9:30:00 | first_name_4 | last_name_4 | first_name_6 | last_name_6 |

mysql insert on duplicate FIELD instead of KEY

-------------------------------------
| user_id | user_name | user_visits |
-------------------------------------
| 1 | foo | 5 |
-------------------------------------
| 2 | bar | 12 |
-------------------------------------
user_id: auto increament, user_visits: default 1
INSERT INTO table (user_name) VALUES ('baz'), ('bar'), ('qux');
the above statement will of course insert 3 new records, as the result:
-------------------------------------
| user_id | user_name | user_visits |
-------------------------------------
| 1 | foo | 5 |
-------------------------------------
| 2 | bar | 12 |
-------------------------------------
| 3 | baz | 1 |
-------------------------------------
| 4 | bar | 1 |
-------------------------------------
| 5 | qux | 1 |
-------------------------------------
but what I'm trying to achieve is:
-------------------------------------
| user_id | user_name | user_visits |
-------------------------------------
| 1 | foo | 5 |
-------------------------------------
| 2 | bar | 13 |
-------------------------------------
| 3 | baz | 1 |
-------------------------------------
| 4 | qux | 1 |
-------------------------------------
so literally,
if field user_name exists, update user_visits, else insert a new record.
is it possible to achieve this with a single insert statement?
Sure there is but it has nothing to do with your insert statement. You need to add a unique index on the user_name column:
create unique index user_name_idx on yourtable (user_name);
Then afterward in your code that tracks the count will have to decide whether to do an insert or an update.
You have to create a key for your username field and then use INSERT ON DUPLICATE query to update the columns values.
For example your query must be,
INSERT INTO table (user_name) VALUES ('baz'), ('bar'), ('qux')
ON DUPLICATE KEY UPDATE user_visits=user_visits+1;
For further reference visit http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
You could leave your INSERT statement as is and implement a trigger that handles your special actions.
See: An Introduction To Triggers