MySQL: Use CASE/ELSE value as join parameter - php

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)

Related

How to select one value from a two table join?

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

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)

MySQL : SELECT all USERS With 2 messages open and clicked after timestamp

I'm making a MySQL script executed in PHP.
So I have 3 tables.
Messages who contains at least 3 000 000 rows
(userid, messageid, timestamp, received, opened, clicked, deliveryid)
Users
(user(unique), profile, profile_actual_timestamp, last_delivery_id_sent)
events_clicked_data who contains at least 2 000 000 rows and detailed clicked event. This user click on this profile(like GAMES) on this message(deliveryId)
(userid, profile, deliveryId, eventDate)
So here is sample data for messages and users tables.
CREATE TABLE messages
(`user_id` varchar(100), `message_id` int, `timestamp` datetime, `received` varchar(5), `opened` varchar(5), `clicked` varchar(5), `delivery_id` int);
INSERT INTO messages
(`user_id`, `message_id`, `timestamp`, `received`, `opened`, `clicked`, `delivery_id`)
VALUES
("BillyStuff", 12,'2016-05-16 00:00:00', 'true', 'true', 'true', 8),
("BillyStuff", 11,'2016-05-14 00:00:00', 'true', 'true', 'true' , 7),
("BillyStuff", 8,'2016-04-03 00:00:00', 'true', 'false', 'false' , 6),
("BillyStuff", 4,'2016-04-02 00:00:00', 'true', 'false', 'false', 5),
("JohnDoe", 15 ,'2016-05-16 00:00:00', 'true', 'true', 'false' , 4),
("JohnDoe", 13 ,'2016-05-14 00:00:00', 'true', 'true', 'true', 3),
("Donnie", 15 ,'2016-05-16 00:00:00', 'true', 'true', 'true' , 4),
("Donnie", 13 ,'2016-05-14 00:00:00', 'true', 'true', 'true', 3)
CREATE TABLE users
(`user_id` varchar(100), `profile` varchar(100), `profile_actual_timestamp` datetime, `last_delivery_id_sent` int);
INSERT INTO users
(`user_id`, `profile`, `profile_actual_timestamp`, `last_delivery_id_sent`)
VALUES
("BillyStuff", "Game", "2016-01-01 00:00:00", 1),
("JohnDoe", "Book", "2016-01-01 00:00:00", 1),
("Donnie", "Book", "2016-05-16 00:00:00", 4)
I want to get users with 2 messages clicked after timestamp (profile_actual_timestamp means last time it was updated) in profile.
In this case I only get BillyStuff because Donnie is already up to date if I check if profile_actual_timestamp.
After this, I need to check by deliveryId and user in events_clicked's table if same categories was clicked.
CREATE TABLE events_clicked_data
(`user_id` varchar(100), `profile` varchar(100), `deliveryId` int, `eventDate` datetime);
INSERT INTO users
(`user_id`, `profile`, `deliveryId`, `eventDate`) VALUES
("BillyStuff", "Book", 8,"2016-01-01 00:00:00"),
("BillyStuff", "Book", 7,"2016-01-01 00:00:00"),
("JohnDoe", "Book", 3,"2016-01-01 00:00:00"),
("Donnie", "Book", 4,"2016-05-16 00:00:00"),
("Donnie", "Game", 3,"2016-05-16 00:00:00")
In this case, i need to update BillyStuff's profile and change it to "Book" instead of "Game" because he clicked on the same categorie twice in his last messages after the last time he was updated (profile_actual_timestamp)
So its been a really good puzzle for me this week and Im wondering if you guys can help me with this one.
originalid = userid (not necessary original, depend on table)
e.name = name of profil clicked like game.
select originalid,
name
from (
select #g := if(#u = originalid, if (#p = name, #g, #g + 1), 1) as grp,
#u := originalid as originalid,
#p := name as name
from (
select u.originalid,
m.message_sendtime_timestamp,
e.name
from bienvenue_nouveau_client_dev u
inner join messages_nouveaux_clients m
on m.originalid = u.originalid
inner join events_clicked_data e
on e.originalId = u.originalid
and e.deliveryId = m.deliveryId
where m.message_sendtime_timestamp >= u.profil_actuel_timestamp
and m.clicked = 'TRUE'
limit 1000000000000000
order by u.originalid,
m.message_sendtime_timestamp desc
) alias
) alias2
where grp = 1
group by originalid, name
having count(*) > 1
Whatever I change I got an error like this:
1250 - Table 'u' from one of the SELECTs cannot be used in global ORDER clause
The first query can be done as follows:
select u.*
from users u
inner join messages m
on m.user_id = u.user_id
where m.timestamp >= u.profile_actual_timestamp
and m.clicked = 'true'
group by u.user_id
having count(m.message_id) > 1
This second query will give you the users that used the same profile in their last two click events, if these events happened both later than the last update:
select user_id,
profile
from (
select #g := 0 + if(#u = user_id, if (#p = profile, #g, #g + 1), 1) as grp,
#u := user_id as user_id,
#p := profile as profile
from (
select u.user_id,
m.timestamp,
e.profile
from users u
inner join messages m
on m.user_id = u.user_id
left join events_clicked_data e
on e.user_id = u.user_id
and e.deliveryId = m.delivery_id
where m.timestamp >= u.profile_actual_timestamp
order by u.user_id,
m.timestamp desc
) alias
) alias2
where grp = 1
group by user_id, profile
having count(*) > 1
SQL fiddle
This query depends on variables, and is a bit risky, in that it must return the innermost results in the specified order, and that it must evaluate the middle select list (with variable assignments) in top-to-bottom order. This happens consistently, but in theory it is not guaranteed.
The (high) limit in the inner query is there to make sure the order by clause is applied, not to actually limit anything.
The variables #u and #p track the user_id and profile. Based on their previous values, the variable #g is calculated: it is reset to 1 whenever the user_id changed. Otherwise it is kept at the same value if also the profile didn't change, and it is incremented when the profile did change.
This way the grp values identify groups in which the profile is the same and uninterrupted in order of descending timestamp. The most recent group per used has number 1, which is the only one of interest in the outer query. The outer query then requires that this group 1 has more than one record (i.e. more than one occurrence of the same profile).

How to do Custom Order in Mysql

Below is my column
1-1
1-2
2-1
2-4
Is It possible to sort in this way - First Column should be be ascending and Second column should be descending
So the result will be
1-4
1-2
2-1
2-1
I don't care if the row mismatch. How can I achieve this (Something like view)
CREATE TABLE IF NOT EXISTS `pipo_orders` (
`ClientID` int(8) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT ,
);
--
-- Dumping data for table `pipo_orders`
--
INSERT INTO `pipo_orders` (`ClientID`, `created_at`, `updated_at`) VALUES
(17, '2014-11-26 16:21:36', '2014-11-26 10:51:36'),
(17, '2014-11-26 16:46:24', '2014-11-26 11:16:24'),
(17, '2014-12-04 16:45:28', '2014-12-04 11:15:28'),
(17, '2014-12-26 12:58:13', '2014-12-26 07:28:13'),
(17, '2014-12-30 14:29:31', '2014-12-30 08:59:31'),
(17, '2014-12-30 14:31:05', '2014-12-30 09:01:05'),
(17, '2015-01-02 12:20:54', '2015-01-02 06:50:54');
If this is what you want it's weird, and I can't think of a reason why you'd want it. In fact, it goes against the fundamental logic of a database...
Nevertheless:
SELECT t1.ClientID
, t1.created_at
, t2.updated_at
FROM pipo_orders t1
JOIN pipo_orders t2
ON 1 = 1
ORDER BY t1.created_at ASC
, t2.update_at DESC
That looks like a horrible thing to do. The values created_at and updated_at are related by being in the same row. You override this relation and join completely unrelated values.
However to technically do this, you would have to order the two columns separately (giving them row numbers) and then join them again:
select
col1.created_at,
col2.updated_at
from
(
select created_at, #rn1 := #rn1 + 1 as rownum
from pipo_orders po
cross join (select #rn1 := 0) as r
order by created_at
) as col1
from
(
select updated_at, #rn2 := #rn2 + 1 as rownum
from pipo_orders po
cross join (select #rn2 := 0) as r
order by updated_at desc
) as col2 using (rownum)
order by rownum;

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;

Categories