mysql - join select data from two different fields in table - mysql

I have two tables:
friends table (UserID, FriendID) and
users table (UserID, FirstName, LastName).
I'm trying to do one SQL query to join and pull all records that have the UserID or FriendID inside friends table equal to the user's ID but pull the FirstName and LastName from the other UserID.
For example
friends table
UserID = 1 | FriendID = 2
UserID = 3 | FriendID = 1
user table
UserID = 1 | FirstName = "Bob" | LastName = "Hope"
UserID = 2 | FirstName = "John" | LastName = "Doe"
UserID = 3 | FirstName = "Bill" | LastName = "Murray"
If I am logged in as Bob(UserID = 1) trying to pull all of my friends user data (FirstName and LastName) in one query by checking if UserID 1 is either a FriendID or UserID inside the friends table. Then join the data for the opposite field that isn't my ID.
Any ideas?

Try this:
SELECT *
FROM users u
WHERE userid IN ( SELECT userid FROM friends WHERE friendid = 1
UNION ALL
SELECT friendid FROM firends WHERE userid = 1);
This will give you:
| USERID | FIRSTNAME | LASTNAME |
---------------------------------
| 2 | John | Doe |
| 3 | Bill | Murray |
SQL Fiddle Demo

Select b.uid as userid, a.firstname, a.lastname
from user a
Inner join (select friendid as uid from friends where userid=:currentUser
Union select userid as uid from friends where friendid=:currentUser) b
On a phone, so may need syntax tweaks.
An optimiser may suggest a different join strategy based on your real data

If I understand your question, then this works
-- set the id of the logged in user
set #logged_in = 1;
-- select all the fields from the user table
select users.* from users
-- joined the friends table on the `FriendID`
inner join friends on friends.FriendID = users.UserID
-- filtered by `UserID` on friends table matching logged in user
and friends.UserID = #logged_in -- logged in id
-- union-ed with the users table
union select * from users
-- filtered by the `UserID` being the logged in user
where users.UserID = #logged_in -- logged in id
Results for #logged_in = 1:
UserID FirstName LastName
2 John Doe
1 Bob Hope
Results for #logged_in = 2:
UserID FirstName LastName
2 John Doe
Test Database Create Code:
--
-- Table structure for table `friends`
--
CREATE TABLE IF NOT EXISTS `friends` (
`UserID` int(11) NOT NULL,
`FriendID` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `friends`
--
INSERT INTO `friends` (`UserID`, `FriendID`) VALUES
(1, 2),
(3, 1);
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE IF NOT EXISTS `users` (
`UserID` int(11) NOT NULL,
`FirstName` varchar(50) NOT NULL,
`LastName` varchar(50) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`UserID`, `FirstName`, `LastName`) VALUES
(1, 'Bob', 'Hope'),
(2, 'John', 'Doe'),
(3, 'Bill', 'Murray');

Related

Selecting unique column values and returning corresponding values in mysql table

SlNo
UserID
points
1
001
Three
2
002
Three
3
001
Three
I have the following table named 'userdata' and I would like to get the points of just the unique usernames.
SELECT points from userdata where USERID==distinct
Is there a functionality in mysql that works similar to == ?
Use Distinct
Query
Select distinct UserId, points
From userdata;
There are some possibilities
IN clause or a INNER JOIN
Latter will be faster on big tables
CREATE TABLE userdata (
`SlNo` INTEGER,
`UserID` INTEGER,
`points` VARCHAR(5)
);
INSERT INTO userdata
(`SlNo`, `UserID`, `points`)
VALUES
('1', '001', 'Three'),
('2', '002', 'Three'),
('3', '001', 'Thr');
SELECT `points` FROM userdata WHERE `UserID` IN (SELECT `UserID` FROM userdata GROUP BY `UserID` HAVING COUNT(*) = 1)
| points |
| :----- |
| Three |
SELECT `points`
FROM userdata u1 INNER JOIN (SELECT `UserID` FROM userdata GROUP BY `UserID` HAVING COUNT(*) = 1) u2 ON u1.`UserID` = u2.`UserID`
| points |
| :----- |
| Three |
db<>fiddle here

How to get one extra record for LEFT JOIN to represent a record not include on the left joined table

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

MYSQL : Group count specific column per user?

I want count column per specific user, using data from 3 tables.
TABLE 1 (users) :
CREATE TABLE `datastore`.`users` ( `uid` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(30) NOT NULL DEFAULT 'john' , `class` VARCHAR(20) NOT NULL DEFAULT 'NEW' , PRIMARY KEY (`uid`)) ENGINE = InnoDB;
INSERT INTO `users` (`uid`, `name`, `class`) VALUES (NULL, 'john', 'NEW'), (NULL, 'mark', 'OLD');
SAMPLE :
uid name class
1 john NEW
2 mark OLD
TABLE 2 (data) :
CREATE TABLE `datastore`.`data` ( `id` INT NOT NULL AUTO_INCREMENT , `source` VARCHAR(30) NULL DEFAULT NULL , `destination` VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
INSERT INTO `data` (`id`, `source`, `destination`) VALUES (NULL, 'NETWORK', 'SERVER_1'), (NULL, 'STATION', 'SERVER_2'), (NULL, 'DATASTORE', 'SERVER_1');
SAMPLE :
id source destination
1 NETWORK SERVER_1
2 STATION SERVER_2
3 DATASTORE SERVER_1
TABLE 3 (access):
CREATE TABLE `datastore`.`access` ( `id` INT(11) NOT NULL AUTO_INCREMENT , `uid` INT(11) NULL DEFAULT NULL , `source` VARCHAR(30) NULL DEFAULT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
INSERT INTO `access` (`id`, `uid`, `source`) VALUES (NULL, '1', 'NETWORK'), (NULL, '2', 'STATION'), (NULL, '1', 'STATION'), (NULL, '1', 'STATION');
SAMPLE :
id uid source
1 1 NETWORK
2 2 STATION
3 1 STATION
4 1 STATION
What i tried so far :
SELECT access.uid, data.destination, COUNT(*) as count FROM data, access WHERE access.source = data.source GROUP BY destination, uid
Result :
uid destination count
1 SERVER_1 1
1 SERVER_2 2
2 SERVER_2 1
I what to link it with user name alse,
Desired Result :
uid name destination count
1 john SERVER_1 1
1 john SERVER_2 2
2 mark SERVER_2 1
Seems you need also a join for users
SELECT access.uid
, users.name
, data.destination
, COUNT(*) as count
FROM data
INNER JOIN access ON access.source = data.source
INNER JOIN users ON users.uid = access.uid
GROUP BY destination, uid, users.name
and as suggestion, you should not use the (old) implicit join syntax based on where .. but the explicit join syntax.
Use aggregation:
select
a.uid,
u.name,
d.destination,
count(*)
from
access a
inner join users u on u.uid = a.uid
inner join data on d.source = a.source
group by
a.uid,
u.name,
d.destination
All you need to get the user's name is to join your query to the table users:
SELECT u.uid, u.name, t.destination, t.count
FROM users u INNER JOIN (
SELECT a.uid, d.destination, COUNT(*) AS count
FROM data d
INNER JOIN access a ON a.source = d.source
GROUP BY d.destination, a.uid
) t ON u.uid = t.uid
ORDER BY u.uid, t.destination
See the demo.
Results:
| uid | name | destination | count |
| --- | ---- | ----------- | ----- |
| 1 | john | SERVER_1 | 1 |
| 1 | john | SERVER_2 | 2 |
| 2 | mark | SERVER_2 | 1 |

Create trigger for several rows

I have table users AND orders. After every UPDATE row in orders. I want update DATA in users table namely concat(OLD.DATA + ID which was updated).
Table 'users'.
ID NAME DATA
1 John 1|2
2 Michael 3|4
3 Someone 5
Table 'orders'.
ID USER CONTENT
1 1 ---
2 1 ---
3 2 ---
4 2 ---
5 3 ---
For example:
SELECT `data` from `users` where `id` = 2; // Result: 3|4
UPDATE `orders` SET '...' WHERE `id` > 0;
**NEXT LOOP**
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 1;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 2;
UPDATE `users` SET `data` = concat(OLD.data, ID.rowUpdated) WHERE `user` = 3;
Result:
SELECT data from users where id = 1; // Result: 1|2|1|2
SELECT data from users where id = 2; // Result: 3|4|3|4
SELECT data from users where id = 3; // Result: 5|5
How can I do it?
I think you are making the same mistake I made not too long ago, ie storing an array/object in a column.
I would recommend using the following tables in your scenario:
users
+-----------+-----------+
| id | user_name |
+-----------+-----------+
| 1 | John |
+-----------+-----------+
| 2 | Michael |
+-----------+-----------+
orders
+-----------+-----------+------------+
| id | user_id |date_ordered|
+-----------+-----------+------------+
| 1 | 1 | 2019-03-05 |
+-----------+-----------+------------+
| 2 | 2 | 2019-03-05 |
+-----------+-----------+------------+
Where user_id is the foreign key to users
sales
+-----------+-----------+------------+------------+------------+
| id | order_id | item_sku | qty | price |
+-----------+-----------+------------+------------+------------+
| 1 | 1 | 1001 | 1 | 2.50 |
+-----------+-----------+------------+------------+------------+
| 2 | 1 | 1002 | 2 | 3.00 |
+-----------+-----------+------------+------------+------------+
| 3 | 2 | 1001 | 2 | 2.00 |
+-----------+-----------+------------+------------+------------+
where order_id is the foreign key to orders
Now for the confusing part. You will need to use a series of JOINs to access the relevant data for each user.
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
WHERE user_id=1
GROUP BY order_id;
This will return:
+-----------+--------------+------------+------------+--------------+
| user_id | user_name | order_id |date_ordered| order_total |
+-----------+--------------+------------+------------+--------------+
| 1 | John | 1 | 2019-03-05 | 8.50 |
+-----------+--------------+------------+------------+--------------+
These type of JOIN statements should come up in basically any project using a relational database (that is, if you are designing your DB correctly). Typically I create a view for each of these complicated queries, which can then be accessed with a simple SELECT * FROM orders_view
For example:
CREATE
ALGORITHM = UNDEFINED
DEFINER = `root`#`localhost`
SQL SECURITY DEFINER
VIEW orders_view AS (
SELECT
t3.id AS user_id,
t3.user_name,
t1.id AS order_id,
t1.date_ordered,
SUM((t2.price * t2.qty)) AS order_total
FROM orders t1
JOIN sales t2 ON (t2.order_id = t1.id)
LEFT JOIN users t3 ON (t1.user_id = t3.id)
GROUP BY order_id
)
This can then be accessed by:
SELECT * FROM orders_view WHERE user_id=1;
Which would return the same results as the query above.
Depending on your needs, you will probably need to add a few more tables (addresses, products etc.) and several more rows to each of these tables. Very often you will find that you need to JOIN 5+ tables into a view, and sometimes you might need to JOIN the same table twice.
I hope this helps despite it not exactly answering your question!
It is probably a bad idea to update the USERS table after inserting into (or updating) the ORDERS table. Avoid storing data twice. In your case: you can always get all "order ids" for a user by querying the ORDERS table. Thus, you don't need to store them in the USERS table (again). Example (tested with MySQL 8.0, see dbfiddle):
Tables and data
create table users( id integer primary key, name varchar(30) ) ;
insert into users( id, name ) values
(1, 'John'),(2, 'Michael'),(3, 'Someone') ;
create table orders(
id integer primary key
, userid integer
, content varchar(3) references users (id)
);
insert into orders ( id, userid, content ) values
(101, 1, '---'),(102, 1, '---')
,(103, 2, '---'),(104, 2, '---'),(105, 3, '---') ;
Maybe a VIEW - similar to the one below - will do the trick. (Advantage: you don't need additional columns or tables.)
-- View
-- Inner SELECT: group order ids per user (table ORDERS).
-- Outer SELECT: fetch the user name (table USERS)
create or replace view userorders (
userid, username, userdata
)
as
select
U.id, U.name, O.orders_
from (
select
userid
, group_concat( id order by id separator '|' ) as orders_
from orders
group by userid
) O join users U on O.userid = U.id ;
Once the view is in place, you can just SELECT from it, and you will always get the current "userdata" eg
select * from userorders ;
-- result
userid username userdata
1 John 101|102
2 Michael 103|104
3 Someone 105
-- add some more orders
insert into orders ( id, userid, content ) values
(1000, 1, '***'),(4000, 1, '***'),(7000, 1, '***')
,(2000, 2, ':::'),(5000, 2, ':::'),(8000, 2, ':::')
,(3000, 3, '###'),(6000, 3, '###'),(9000, 3, '###') ;
select * from userorders ;
-- result
userid username userdata
1 John 101|102|1000|4000|7000
2 Michael 103|104|2000|5000|8000
3 Someone 105|3000|6000|9000

Values not being inserted into the right record/s MySQL

When inserting a value into a table, instead of just assigning the value to the single record it is assigning it to all of them:
CREATE TABLE IF NOT EXISTS user
(
userID VARCHAR(50) NOT NULL,
userName VARCHAR(40) NOT NULL,
PRIMARY KEY (userID)
);
CREATE TABLE IF NOT EXISTS rewards
(
rewardID VARCHAR(5) NOT NULL,
rewardDescription VARCHAR(20) NOT NULL,
PRIMARY KEY (rewardID)
);
CREATE TABLE IF NOT EXISTS promos
(
issueDate DATETIME DEFAULT CURRENT_TIMESTAMP,
expiryDateOfReward DATETIME GENERATED ALWAYS AS(issueDate + INTERVAL 6 MONTH),
quantity BIT DEFAULT 0,
userID VARCHAR(50) REFERENCES user(userID),
rewardID VARCHAR(5) REFERENCES rewards(rewardsID),
PRIMARY KEY(userID, rewardID)
);
INSERT INTO user
VALUES('DV2015', 'Bob'),
('DV2016', 'Mary'),
('DV2017', 'Megan');
INSERT INTO rewards
VALUES('rw10', '10% Off'),
('rw20', '20% Off'),
('rw30', '30% Off');
INSERT INTO promos (issueDate, quantity, userID, rewardID)
VALUES(DEFAULT, DEFAULT, 'DV2016', 'rw10'),
(DEFAULT, 1, 'DV2015', 'rw20'),
(DEFAULT, DEFAULT, 'DV2017', 'rw30');
I am then selecting it:
SELECT userName,
rewards.rewardID,
rewards.rewardDescription,
promos.quantity
FROM user
JOIN promos
ON user.userID = promos.userID
JOIN rewards
ON promos.rewardID = promos.rewardID
WHERE user.userID = 'DV2015';
I don't know if the error is in the creation of the tables, me inserting records, or in the select statement.
This is how it outputs:
I only want Bob with a userID of 'DV2015' to have a 'quantity' of 1 on 'rw20'. rw10 and rw30 must still have a quantity of 0. However when inserting data it is giving him all the rewards
You've got an accidental cross join here:
JOIN rewards
ON promos.rewardID = promos.rewardID
Which is of course true for all promos records. You probably meant:
JOIN rewards
ON rewards.rewardID = promos.rewardID
If you want "rw10 and rw30 must still have a quantity of 0" you should switch to LEFT JOIN rather than (INNER) JOIN.
Join from the promos table instead
SELECT u.userName,
r.rewardID,
r.rewardDescription,
p.quantity
FROM promos p
JOIN user u ON u.userID = p.userID
JOIN rewards r ON r.rewardID = p.rewardID
WHERE u.userID = 'DV2015';
you did mistake to insert data into table
insert into table_name values()//if you provide values all the column
otherwise
insert into table_name (col1,col2...coln) values('use1','use2'....n)
so in your case in user table value will be insert like below
INSERT INTO user values(1,'user_name1'),(2,'user_name2');
Other two tables (rewards,promos) data insertion process is valid
From your expect result you can try this.
CROSS JOIN by user and rewards tables to descartes products result.
Then promos tables OUTER JOIN base on CROSS JOIN result set.
CREATE TABLE user
(
userID VARCHAR(50) NOT NULL,
userName VARCHAR(40) NOT NULL
);
CREATE TABLE rewards
(
rewardID VARCHAR(5) NOT NULL,
rewardDescription VARCHAR(20) NOT NULL
);
CREATE TABLE promos
(
issueDate DATETIME DEFAULT CURRENT_TIMESTAMP,
expiryDateOfReward DATETIME ,
quantity int DEFAULT 0,
userID VARCHAR(50),
rewardID VARCHAR(5)
);
INSERT INTO user
VALUES('DV2015', 'Bob'),
('DV2016', 'Mary'),
('DV2017', 'Megan');
INSERT INTO rewards
VALUES('rw10', '10% Off'),
('rw20', '20% Off'),
('rw30', '30% Off');
INSERT INTO promos (issueDate, quantity, userID, rewardID)
VALUES(0, 0, 'DV2016', 'rw10'),
(0, 1, 'DV2015', 'rw20'),
(0, 0, 'DV2017', 'rw30');
Query 1:
SELECT userName,
r.rewardID,
r.rewardDescription,
coalesce(p.quantity,0) quantity
FROM user u CROSS JOIN rewards r
LEFT JOIN promos p
ON u.userID = p.userID and r.rewardID = p.rewardID
WHERE u.userID = 'DV2015'
Results:
| userName | rewardID | rewardDescription | quantity |
|----------|----------|-------------------|----------|
| Bob | rw20 | 20% Off | 1 |
| Bob | rw10 | 10% Off | 0 |
| Bob | rw30 | 30% Off | 0 |