How to select one value from a two table join? - php

I'm trying to achieve something in Laravel/MySQL and cannot seem to be pointed in the right direction for a solution. I can achieve what I am looking for with subqueries, but I have been told they are not as efficient as joins. And, I'm going to have to convert the solution for this into Eloquent/Query Builder, and the way I have it working with subqueries and unions doesn't seem to convert easily.
What I am trying to do is select one row from two possible tables, based on the created_at date of the row. I want to join this created_at value with my users table as a new column called started_at. Here is some sample data and how I can achieve the query with a subquery/union of the two possible tables that I can get the data from:
CREATE TABLE users (
id INTEGER,
first_name TEXT,
last_name TEXT
);
INSERT INTO users (id, first_name, last_name)
VALUES
(1, 'Craig', 'Smith'),
(2, 'Bill', 'Nye'),
(3, 'Bloop', 'Blop');
CREATE TABLE old_activity (
id INTEGER,
user_id INTEGER,
firm_id INTEGER,
amount INTEGER,
created_at DATE
);
INSERT INTO old_activity (id, user_id, firm_id, amount, created_at)
VALUES
(1, 1, 3, 5.24, '2019-04-29'),
(2, 2, 7, 4, '2019-03-28'),
(3, 3, 4, 6.99, '2019-04-28');
CREATE TABLE new_activity (
id INTEGER,
user_id INTEGER,
firm_id INTEGER,
plays INTEGER,
saves INTEGER,
created_at DATE
);
INSERT INTO new_activity (id, user_id, firm_id, plays, saves, created_at)
VALUES
(1, 1, 3, 10, 1, '2019-04-27'),
(2, 2, 3, 12, 2, '2019-03-29'),
(3, 3, 3, 6, 3, '2019-04-27');
CREATE TABLE firms (
id INTEGER,
name TEXT
);
INSERT INTO firms (id, name)
VALUES
(1, 'apple'),
(2, 'banana'),
(3, 'orange');
select
id,
first_name,
last_name,
(select created_at from old_activity
where user_id = users.id
union
select created_at from new_activity
where user_id = users.id
order by created_at asc
limit 1) as started_at
from users
The query should only return the oldest created_at for a particular user in one of the two activity tables.
How can I achieve this with a join? Any help with this would be greatly appreciated.

Hmmm . . . You could always use window functions:
select u.*, a.*
from users u left join
(select a.*,
row_number() over (partition by a.user_id order by a.created_at desc) as seqnum
from ((select oa.* from old_activity oa) union all
(select na.* from new_activity na)
) a
) a
on a.user_id = a.id and a.seqnum = 1

Related

How to have a union in subquery MySQL Laravel?

I'm trying to perform a subquery in Laravel to get some relevant user data, but the data may be in one of two tables. I have a table for test_activity and live_activity which both have a created_at column. My goal is to get the oldest record from a combination of the data from the two tables.
Sample data:
CREATE TABLE users (
id INTEGER,
first_name TEXT,
last_name TEXT
);
INSERT INTO users (id, first_name, last_name)
VALUES (1, 'Craig', 'Smith'), (2, 'Bill', 'Nye'), (3, 'Bloop', 'Blop');
CREATE TABLE test_activity (
id INTEGER,
user_id INTEGER,
created_at DATE
);
INSERT INTO test_activity (id, user_id, created_at)
VALUES (1, 1, '2019-04-29'), (2, 2, '2019-03-28'), (3, 3, '2019-04-28');
CREATE TABLE live_activity (
id INTEGER,
user_id INTEGER,
created_at DATE
);
INSERT INTO live_activity (id, user_id, created_at)
VALUES (1, 1, '2019-04-27'), (2, 2, '2019-03-29'), (3, 3, '2019-04-27');
Here is how I am trying the query with Laravel:
$firstActivity = TestActivity::select('created_at')
->whereColumn('user_id', 'id');
$firstActivity = LiveActivity::select('created_at')
->whereColumn('user_id', 'id')
->union($firstActivity)
->limit(1)
->getQuery();
$users = Users::select(['id', 'first_name', 'last_name'])
->whereIn('id', $arrayOfIds)
->selectSub($firstActivity, 'start_date')
->paginate(25);
Here is the query as it's being executed and throwing an error:
select
`id`,
`first_name`,
`last_name`,
((select `created_at`
from `live_activity`
where `user_id` = `id`)
union
(select `created_at`
from `test_activity`
where `user_id` = `id`
limit 1) as `start_date`
from `users`
where `id` in (....)
limit 25
offset 0)
The error I get is this
Query 1 ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'union
How can I make this work? And, is there a better, more efficient way to perform this kind of query with Laravel? Thank you.
UPDATE:
I've provided sample data for anyone willing to help. Here is the query I am now trying to run:
select
id,
first_name,
last_name,
(select created_at
from test_activity
where user_id = users.id
union
select created_at
from live_activity
where user_id = users.id
order by created_at asc
limit 1) as start_date
from users;
How can I convert this to eloquent?
In a lot of cases, joins are faster than sub-queries. I recommend working with sql, and testing what is faster before writing it in Laravel, since you aren't using the ORM anyway, and then just rewrite the best query using the query building language.
The join approach would join all three columns, using join where MAX(created_at) on both the testactivity and liveactivity and then using select GREATEST(test_Created_at, live_create_at) as start_date

Not able to get distinct and max of the third table join in MySql

Schemas
// First table
CREATE TABLE assignments (
id int,
uid int,
comments varchar(255),
assignmentdate date,
status int
);
INSERT INTO assignments (id, uid, comments, assignmentdate, status)
values (1, 6, 'a', '2019-07-15', 0), (2, 6, 'ab', '2019-07-15', 0),
(3, 6, 'abc', '2019-07-14', 0), (4, 6, 'abc', '2019-07-14', 1)
, (5, 7, 'xyz', '2019-07-14', 1), (6, 7, 'zyx', '2019-07-14', 1);
// Second table
CREATE TABLE users (
id int,
username varchar(255),
status int
);
INSERT INTO users (id, username, status)
values (6, 'user1', 0), (7, 'user2', 0),
(8, 'user3', 1);
// Third table
CREATE TABLE user_images (
id int,
uid int,
imagename varchar(255),
status int
);
INSERT INTO user_images (id, uid, imagename, status)
values (1, 6, 'abc.jpeg', 0), (2, 6, 'def.jpeg', 0), (3, 8, 'ghi.png', 1);
what I'm looking for here is to get
1) distinct and latest row of table assignments which,
2) joins the table users and get a row and then joins,
3) distinct and latest row of table user_images.
So far i have gone through this answer
My trial query:
SELECT
p.*,
u.username,
groupedpi.*
FROM
assignments p
INNER JOIN(
SELECT
comments,
MAX(id) AS latest
FROM
assignments
WHERE
STATUS
= 0
GROUP BY
uid
) AS groupedp
ON
groupedp.latest = p.id
LEFT JOIN users u ON
p.uid = u.id AND u.status = 0
LEFT JOIN(
SELECT
uid,
MAX(id) AS latesti,
imagename
FROM
user_images us
WHERE
STATUS = 0
GROUP BY
uid
order by id desc LIMIT 1
) AS groupedpi
ON
groupedpi.uid = p.uid
Output:
The 3rd result I'm not getting, i.e I'm not getting the distinct and latest record of the third table while joining.
Instead of abc.jpeg, I want to get def.jpeg.
MySQL is tripping you up here, because it automatically adds columns to GROUP BY if they aren't specified, so it's grouping the groupedpi subquery on imagename too - this will lead to duplicated rows. Remove the imagename column from the subquery (and the order by clause is irrelevant too) and have it just output the userid and the max image id
If you want the image name, join the images table in again on images.id = groupedpi.latesti (In the main query not the subquery that is finding the latest image id)
(Note that your screenshot says lastesti 2 but imagename abc- it's not the right pairing. ID 2 is def.jpg. When you want latest Id but also other data from the same row you can't do it in one hit unless you use an analytic (mysql8+) - you have to write a subquery that finds the max id and then join it back to the same table to get the rest of the data off that row)

How to select all columns from a table but with unique one column

I have a table with a lot of columns and I want to select all columns but I want unique one of these columns.
This works for me but I don't get all columns:
$result = mysql_query("SELECT DISTINCT company FROM table t $order");
I also tested this but doesn't do anything:
$result = mysql_query("SELECT * DISTINCT company FROM table t $order");
$result = mysql_query("SELECT DISTINCT company * FROM table t $order");
EDIT
My table has a lot of columns let's say it has 5 so an example of my records is this:
company x y price numOfEmpl
one 1 5 1.3 15
one 2 6 1.4 15
two 3 7 1.5 16
three 4 8 1.6 17
So I want to cut the second line and take all the others.
The DISTINCT keyword can be used to return only distinct (different) values within defined columns, like
SELECT DISTINCT column_name,column_name
or you can return the amount of DISTINCT values by
SELECT COUNT(DISTINCT column_name)
See some samples on W3Schools
I think you might need to use a seperate sql query for all the records
Edited previous answer based on extra information
Have a solution for you in MySQL and in SQL Server if you need it
MySQL Example (Using User Variables/Sub-Queries)
CREATE TABLE SomeTable (company VARCHAR(20), x INT, y INT, price FLOAT, numOfEmploy INT);
INSERT INTO SomeTable
(company, x, y, price, numOfEmploy)
VALUES
('one', 1, 5, 1.3, 15),
('one', 1, 6, 1.4, 15),
('two', 1, 7, 1.5, 16),
('three', 1, 8, 1.6, 17);
SET #count = NULL, #value = NULL;
SELECT company, x, y, price, numOfEmploy FROM (
SELECT
company, x, y, price, numOfEmploy,
#count := IF(#value = company, #count + 1, 1) AS rc,
#value := company
FROM SomeTable
) AS grouped_companies WHERE rc = 1
SQL Server Example (Using CTE)
--Create the table
declare #sometable table ( company varchar(10), x int, y int, price float, numOfEmploy int)
--insert the data
insert into #sometable values ('one', 1, 5, 1.3, 15)
insert into #sometable values ('one', 2, 6, 1.4, 15)
insert into #sometable values ('two', 3, 7, 1.5, 16)
insert into #sometable values ('three', 4, 8, 1.6, 17)
--WITH Common Table Expression
;WITH grouped_companies AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY company
ORDER BY company) AS rc
FROM #sometable)
SELECT gc.company, gc.x, gc.y, gc.price, gc.numOfEmploy
FROM grouped_companies gc
WHERE gc.rc = 1

pulling data from mysql db where users have multiple rows but looking for the row with two maximum values

I have a table called leaders. in it I have id,userid,bmid,reps,rounds,ts. Basically I need to put the top ten unique users out of the table that have the most rounds. Now each time someone enters their reps,rounds its as a pair so someone might have 12 rounds 13 reps so if that is their max and it is within the top ten of all users then I need to pull that info plus their corresponding reps. I thought I had this but it is actually pulling their max rounds and their max reps from different rows. What I have is below.
SELECT max(l.rounds) as rounds, l.reps, m.name, l.userid
from leaders l
inner join members m on m.id = l.userid
where m.genre = 'male' and l.bmid = 1
group by l.userid
order by rounds desc,reps desc
the join is to the members table to get some info on them.
I guess you need a subquery to achieve this. Take a look at this:
How to select the first/least/max row per group in SQL
EDIT: Try this code: http://sqlfiddle.com/#!2/8bb81/3
CREATE TABLE `leaders` (
`id` int(11) AUTO_INCREMENT,
`userid` int(11),
`rounds` int(11),
`reps` int(11),
PRIMARY KEY (`id`)
);
INSERT INTO `leaders` (userid, rounds, reps) VALUES
(1, 5, 3), (1, 7, 2), (1, 7, 1), (1, 7, 8),
(2, 7, 6), (2, 7, 9), (2, 4, 3),
(3, 7, 2), (3, 3, 5),
(4, 8, 9);
SELECT
userid,
MAX(rounds) AS max_rounds, (
SELECT MIN(reps)
FROM leaders l2
WHERE l2.userid = l1.userid AND l2.rounds <> l1.rounds
) AS min_reps
FROM leaders l1
GROUP BY userid
ORDER BY max_rounds DESC, min_reps ASC;

MySQL: Use CASE/ELSE value as join parameter

I'm trying to join the NAME and PHOTO from USERS table to the TRANSACTIONS table based on who is the payer or payee. It keeps telling me can't find the table this -- What am I doing wrong?
SELECT `name`,`photo`,`amount`,`comment`,
(
CASE `payer_id`
WHEN 72823 THEN `payee_id`
ELSE `payer_id`
END
) AS `this`
FROM `transactions`
RIGHT JOIN `users` ON (`users`.`id`=`this`)
WHERE `payee_id`=72823 OR `payer_id`=72823
From the documentation about aliases:
The alias is used as the expression's column name and can be used in GROUP BY, ORDER BY, or HAVING clauses.
You can't use an alias in a join. You can use it only in the places listed above. The reason is that the alias is on a field in the result of the join. If the join were allowed to these aliases in its definition it would (or could) result in recursive definitions.
To solve your problem you could repeat the CASE clause in both places:
SELECT `name`,`photo`,`amount`,`comment`,
(
CASE `payer_id`
WHEN 72823 THEN `payee_id`
ELSE `payer_id`
END
) AS `this`
FROM `transactions`
RIGHT JOIN `users` ON `users`.`id`= (
CASE `payer_id`
WHEN 72823 THEN `payee_id`
ELSE `payer_id`
END
)
WHERE `payee_id`=72823 OR `payer_id`=72823
However I would probably rewrite this query as two selects and UNION them:
SELECT name, photo, amount, comment, payer_id AS this
FROM transactions
JOIN users ON users.id = payer_id
WHERE payee_id = 72823
UNION ALL
SELECT name, photo, amount, comment, payee_id AS this
FROM transactions
JOIN users ON users.id = payee_id
WHERE payer_id = 72823
Result:
'name3', 'photo3', 30, 'comment3', 3
'name1', 'photo1', 10, 'comment1', 1
'name2', 'photo2', 20, 'comment2', 2
Test data:
CREATE TABLE users (id INT NOT NULL, name NVARCHAR(100) NOT NULL, photo NVARCHAR(100) NOT NULL);
INSERT INTO users (id, name, photo) VALUES
(1, 'name1', 'photo1'),
(2, 'name2', 'photo2'),
(3, 'name3', 'photo3'),
(4, 'name4', 'photo4');
CREATE TABLE transactions (amount INT NOT NULL, comment NVARCHAR(100) NOT NULL, payer_id INT NOT NULL, payee_id INT NOT NULL);
INSERT INTO transactions (amount, comment, payer_id, payee_id) VALUES
(10, 'comment1', 72823, 1),
(20, 'comment2', 72823, 2),
(30, 'comment3', 3, 72823),
(40, 'comment4', 4, 5);
SELECT
th.id, th.coin_id, th.coin_family, cm.coin_id, cm.current_price
FROM
trnx_history th
JOIN
fmi_coins.coins_markets cm
ON
cm.coin_id=(CASE th.coin_family WHEN 1 THEN 1 ELSE 2 END)

Categories