I have a question. This is my database structure
**company**
id | name
---------
1, Test
2, demo
**address**
id | name
---------
1, test1
2, test2
3, bla6
**address_company**
id | address_id | company_id
1, 1, 1
2, 2, 1
3, 3, 2
My query is this:
SELECT company.name, address.name FROM company
INNER JOIN address_company on address_company.company_id = company.id
INNER JOIN address on address.id = address_company.address_id
This works. But I need to filter results.
So when people click address (frontend): test1, it only needs to show company: Test
I can do this:
WHERE address.name = "test1"
This also works but I need to filter further so what I need is
WHERE address.name = "test1" AND address.name = "test2"
But this doesn't work, it doesn't show results. I can only filter on 1 address and I need to filter on more addresses.
Hope you guys can understand me and can help me.
THANKS!
The below strategy leans on the unique key(address_id,company_id) making sure there are no duplicates at that combo-level
Schema
create table company
( id int auto_increment primary key,
name varchar(100) not null
);
insert company(name) values ('Test'),('demo');
create table address
( id int auto_increment primary key,
name varchar(100) not null
);
insert address(name) values ('test1'),('test2'),('bla6');
create table address_company
( id int auto_increment primary key,
address_id int not null,
company_id int not null,
unique key(address_id,company_id) -- no dupes allowed ! I am banking on this below
);
insert address_company(address_id,company_id) values (1,1),(2,1),(3,2);
The queries
select company_id,count(*) theCount from address_company
where address_id in (1,2)
group by company_id having theCount>1;
+------------+----------+
| company_id | theCount |
+------------+----------+
| 1 | 2 |
+------------+----------+
select company_id,count(*) theCount from address_company
where address_id in (select id from address where name in ('test1','test2'))
group by company_id having theCount>1;
+------------+----------+
| company_id | theCount |
+------------+----------+
| 1 | 2 |
+------------+----------+
So if the group by / having returns greater than 1 for the count, where I literally went after name1 and name2, then I know that row qualifies. And that row of course then has name1 and name2.
Back to the unique key part: This assures we aren't tricked in having a company with the same address twice. Which first off doesn't make sense, and also that would mess up this strategy.
Obviously the schema needs some index help, and FK's wouldn't break anyone's heart. But this is just a strawman.
Use OR instead of and, or use the in() structure:
WHERE address.name = 'test1' OR address.name = 'test2'
WHERE address.name IN('test1', 'test2' )
Note: I hope that the below join condition was just typed incorrectly in the question:
INNER JOIN address on address.id = address_company.id
Related
I need to get all rows that are in the table A, but joining with the table B (basically a LEFT JOIN), but also, I need to get the A table row itself, for example, with these tables:
Table A:
id
name
1
Random name
2
Random name #2
Table B:
id
parent_id
location
1
2
Location #1
2
2
Location #2
With this query:
SELECT * FROM A
LEFT JOIN B
ON A.id = B.parent_id;
I get something like this:
id
name
id
parent_id
location
1
Random name
NULL
NULL
NULL
2
Random name #2
1
2
Location #1
2
Random name #2
2
2
Location #2
But I want to get something like this:
id
name
id
parent_id
location
1
Random name
NULL
NULL
NULL
2
Random name #2
NULL
NULL
NULL
2
Random name #2
1
2
Location #1
2
Random name #2
2
2
Location #2
As you can see, there is a row by itself of "Random name #2" separated from its joins, how can I do that?
The main idea is that there are an ads table (the table A), but also, there are a subads table (the table B) with little variations of the ads table, and I need to show all ads and subads in a unique query.
Tanks a lot!
Two suggestions:
SELECT * FROM A
INNER JOIN B
ON A.id = B.parent_id
UNION ALL
SELECT *, NULL, NULL, NULL FROM A
or
SELECT A.*,B.*
FROM (SELECT 1 A_ONLY UNION ALL SELECT 0) A_ONLY
CROSS JOIN A
LEFT JOIN B
ON A.id = B.parent_id AND NOT A_ONLY
WHERE A_ONLY OR B.parent_id
The latter is an approach you can use to emulate WITH ROLLUP when that isn't allowed or when you want something slightly different than that produces (here, avoiding a grand total record and avoiding a double record when there are no B rows).
Probably not the best implementation, but until someone comes up with a proper solution...
SELECT A.id, name, B.id, parent_id, location FROM A
LEFT JOIN B
ON A.id = B.parent_id;
UNION ALL
SELECT A.id, name, NULL as id, NULL as parent_id, NULL as location FROM A
WHERE A.id IN (SELECT parent_id FROM B)
Simply UNION ALL with another query taking the values from A that had matches on B, hence no NULL values from the first query.
you need only the NULL added rows from A and the rest of the inner JOIN
CREATE TABLE A
(`id` int, `name` varchar(14))
;
INSERT INTO A
(`id`, `name`)
VALUES
(1, 'Random name'),
(2, 'Random name #2')
;
CREATE TABLE B
(`id` int, `parent_id` int, `location` varchar(11))
;
INSERT INTO B
(`id`, `parent_id`, `location`)
VALUES
(1, 2, 'Location #1'),
(2, 2, 'Location #2')
;
(SELECT A.id as a_id,A.name,B.* FROM A
INNER JOIN B
ON A.id = B.parent_id)
UNION
(SELECT A.*,NULL,NULL,NULL FROM A)
ORDER by a_id,id;
a_id | name | id | parent_id | location
---: | :------------- | ---: | --------: | :----------
1 | Random name | null | null | null
2 | Random name #2 | null | null | null
2 | Random name #2 | 1 | 2 | Location #1
2 | Random name #2 | 2 | 2 | Location #2
db<>fiddle here
You can make INNER JOIN instead of LEFT JOIN and UNION ALL with table A content:
Both queries must return the same number of columns.
SELECT *, NULL, NULL, NULL
FROM A
UNION ALL
SELECT *
FROM A
INNER JOIN B ON A.id = B.parent_id;
I have a database with two tables one table (shops) has an admin user column and the other a user with less privileges. I plan to LEFT JOIN the table of the user with less privileges. When I retrieve the data, the records for the admin user must be on a separate row and must have NULL values for the left joined table followed by records of users with less privileges (records of the left joined table) if any. I am using MySQL.
I have looked into the UNION commands but I don't think it can help. Please see the results bellow of what I need.
Thank you.
SELECT *
FROM shops LEFT JOIN users USING(shop_id)
WHERE shop_id = 1 AND (admin_id = 1 OR user_id = 1);
+---------+----------+---------+
| shop_id | admin_id | user_id |
+---------+----------+---------+
| 1 | 1 | NULL | <-- Need this one extra record
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 3 |
+---------+----------+---------+
Here is an example structure of the databases and some sample data:
CREATE SCHEMA test DEFAULT CHARACTER SET utf8 ;
USE test;
CREATE TABLE admin(
admin_id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(admin_id)
);
CREATE TABLE shops(
shop_id INT NOT NULL AUTO_INCREMENT,
admin_id INT NOT NULL,
PRIMARY KEY(shop_id),
CONSTRAINT fk_shop_admin FOREIGN KEY(admin_id) REFERENCES admin (admin_id)
);
CREATE TABLE users(
user_id INT NOT NULL AUTO_INCREMENT,
shop_id INT NOT NULL,
CONSTRAINT fk_user_shop FOREIGN KEY(shop_id) REFERENCES admin (shop_id)
);
-- Sample data
INSERT INTO admin() VALUES ();
INSERT INTO shops(admin_id) VALUES (1);
INSERT INTO users(shop_id) VALUES (1),(1),(1);
I think you need union all:
select s.shop_id, s.admin_id, null as user_id
from shops s
where s.shop_id = 1
union all
select s.shop_id, s.admin_id, u.user_id
from shops s join
users u
on s.shop_id = u.shop_id
where shop_id = 1;
Put your where condition in On clause
SELECT *
FROM shops LEFT JOIN users on shops.shop_id=users.shop_id and (admin_id = 1 OR user_id = 1)
WHERE shops.shop_id = 1
This query:
SELECT contacts.name, accounts.account
FROM contacts
LEFT JOIN deals
ON contacts.id = deals.contact_id
LEFT JOIN
accounts ON accounts.deal_id = deals.id;
returns:
+------+-------------------+
| name | account |
+------+-------------------+
| Bob | fun deal account |
| Bob | NULL |
| John | NULL |
+------+-------------------+
But I expected:
+------+-------------------+
| name | account |
+------+-------------------+
| Bob | fun deal account |
| Bob | fun deal account |
| John | NULL |
+------+-------------------+
The first LEFT JOIN behaves correctly. Since there are two deals for Bob, Bob correctly shows up twice in result set. But the second LEFT JOIN does not behave right, because the account should have been carried over twice for both Bob records, but instead there is a NULL for the second bob.
The schema:
CREATE TABLE contacts(
id int AUTO_INCREMENT,
name VARCHAR(50),
Primary Key(id)
)
INSERT INTO contacts VALUES('Bob');
INSERT INTO contacts(name) VALUES('John');
CREATE TABLE deals(
id int AUTO_INCREMENT,
name VARCHAR(20),
contact_id int,
FOREIGN KEY(contact_id) REFERENCES contacts(id),
Primary Key(id)
);
INSERT INTO deals(name, contact_id) VALUES('cool deal',1);
INSERT INTO deals(name, contact_id) VALUES('another cool deal',1);
CREATE TABLE accounts(
id int AUTO_INCREMENT,
account VARCHAR(50),
deal_id int,
FOREIGN KEY(deal_id) REFERENCES deals(id),
PRIMARY KEY (id)
)
INSERT INTO accounts(account, deal_id) VALUES('fun deal account', 1);
Why doesn't the second LEFT JOIN give desired behavior and how can I get the 'fun deal account' account to show up for both Bobs?
Bob have two deals but deals.id is auto_increment so fun deal account only match the first row in deals table, the cool deal.
You need to add INSERT INTO accounts(account, deal_id) VALUES('fun deal account', 2); too
In case of doubts, decompose your query.
The first LEFT JOIN could be this:
SELECT contacts.id as contact_id, contacts.name, deals.id as deals_id, deals.name
FROM contacts
LEFT JOIN deals ON contacts.id = deals.contact_id
Which results in :
contact_id name deals_id name
1 Bob 1 cool deal
1 Bob 2 another cool deal
2 John NULL NULL
The second LEFT JOIN is:
LEFT JOIN accounts ON accounts.deal_id = deals.id
So the result given is logical given your data, you have only one account with deal_id=1 so it matches the first row where deals.id=1 : "cool deal" .
I think your mistake is on the last part of your query, the query you wanted is :
SELECT contacts.name, accounts.account FROM contacts LEFT JOIN deals ON contacts.id = deals.contact_id LEFT JOIN accounts ON accounts.deal_id = deals.contact_id
"accounts.deal_id = deals.contact_id" instead of "accounts.deal_id = deals.id" is the deal (pun intended) to have your expected result.
I have multiple tables (Eg : A,B,C....etc with Foreign Key ADDRESS_ID) linked to 'address' table where ADDRESS_ID is the primary key. Tables A,B,C... may have address_ID value or not. Is there a query that I could get table names with given ADDRESS_ID in address table ? Note that this is not table names for a given column in 'address' table. I need table names for a given Address_ID value in address table if it is already referenced in other tables. (I do not know all tables that may have address_id referenced.)
Address table
ADDRESS_ID (PK) | STREET ADDRESS_1 | STREET_ADDRESS_2 | CITY | ZIPCODE
Table A
A_ID | ADDRESS_ID (FK) | FIELD _1 | FIELD_2
Table B
B_ID | ADDRESS_ID (FK)| FIELD _! | FIELD_2
Table C
B_ID | ADDRESS_ID (FK) | FIELD _! | FIELD_2
more tables ......
This is what I tried :
SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE
information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' AND
TABLE_CONSTRAINTS.TABLE_SCHEMA = 'flower_db' AND
TABLE_CONSTRAINTS.TABLE_NAME = 'address';
But this does not support what I exactly wanted.
SELECT *
FROM information_schema.REFERENTIAL_CONSTRAINTS
WHERE REFERENCED_TABLE_NAME = 'address'
(adjust the query as needed)
SELECT 'TableA' as Table_Has_Address
FROM TableA
GROUP BY ADDRESS_ID
HAVING COUNT(CASE WHEN ADDRESS_ID = #address_id THEN 1 END) > 0
UNION ALL
SELECT 'TableB' as Table_Has_Address
FROM TableB
GROUP BY ADDRESS_ID
HAVING COUNT(CASE WHEN ADDRESS_ID = #address_id THEN 1 END) > 0
UNION ALL
SELECT 'TableC' as Table_Has_Address
FROM TableC
GROUP BY ADDRESS_ID
HAVING COUNT(CASE WHEN ADDRESS_ID = #address_id THEN 1 END) > 0
I've the following three tables:
Table A:
id VARCHAR(32) | value VARCHAR(32) | groupId INT
abcdef | myValue1 | 1
ghijkl | myValue2 | 2
mnopqr | myValue3 | 1
Table B:
id VARCHAR(32) | value VARCHAR(32) | userId INT
abcdef | myValue4 | 1
uvwxyz | anotherValue | 1
Table C:
id VARCHAR(32) | someOtherColumns...
abcdef
ghijkl
mnopqr
...
uvwxyz
Table A and B are used for a m:n-association, thus the "id"-column in both tables references the same field ("id"-column in table c).
What I want to do is (for instance)... select all entries in table A where groupId = 1
SELECT * FROM TableA WHERE groupId = 1
and also select all entries in table B where userId = 1
SELECT * FROM TableB WHERE userId = 1
That's all no problem... but the following makes the select-statement(s) difficult: How can I merge both select-results and replace the value of the first result? For example:
selecting all entries in Table A where groupId = 1 I'll get abcdef and also mnopqr.
when I select all entries in Table B where userId = 1 I'll also get abdef (and additionally uvwxyz).
Now, the value of abcdef in Table B should replace the value in the selection result of table A. And the uvwxyz-entry should be added to the result.
Finally I'm looking for a query which produces the following table:
id VARCHAR(32) | value VARCHAR(32)
abcdef | myValue4 -- myValue1 from the select-statement in tableA should be overwritten
mnopqr | myValue2 -- from table A
uvwxyz | anotherValue -- from table B
I hope anyone know how to do this... thanks in advance for any suggestion! By the way... it would be great if there is any chance to realize this using one single (long) select statement.
Try this:
SELECT * FROM TableB WHERE userId = 1
UNION
SELECT * FROM TableA WHERE groupId = 1
and id not in (select id from TableB where userid = 1)
#rs points out to use the UNION, which is required since MySQL doesn't have FULL joins.
Favoring the data from table B is a chose for CASE:
select id, case when max(value_b) is not null then max(value_b) else max(value_a) end as final_value
from (
select id, value as 'value_a', null as 'value_b' from tableA
union
select id, null, value from tableB
) ugh
group by 1;