SQL Inner join with limit on subquery not to affect query - mysql

I'm running a query in MySQL with an INNER JOIN that has a LIMIT on the subquery
The problem is, that the LIMIT on the subquery is affecting the number of rows returned.
I want to select all rows from table 1 (tickets) where the last row in ticket_updates relevant (t.ticketnumber = tu.ticketnumber) was not numeric in column contact_name
SELECT t.*
FROM tickets t
JOIN
( SELECT ticketnumber
FROM ticket_updates
WHERE type = 'update'
AND concat('', contact_name * 1) <> contact_name
ORDER
BY sequence DESC
LIMIT 1
) tu
ON t.ticketnumber = tu.ticketnumber
WHERE t.status <> 'Completed'
AND LOWER(t.department) = 'support';
But the results shown just return the 1 row
There are multiple rows in ticket_updates that relate to each row in tickets based on tickets.ticketnumber =ticket_updates.ticketnumber`
the contact_name column can either be a string or integer. I picked up the concat('', contact_name * 1) <> contact_name from another SO Post which tells me whether the value is numeric or not.
So I want to pick up the latest row (ORDER BY sequence DESC) in ticket_updates for each row in tickets and see whether contact_name is not numeric

Your query selects one row because (as you concluded yourself) your subquery is limited to one result.
what you want is probably something similar to what can be found in this answer (I assumed that you want the entry with biggest value for sequence, if the contratry, change to MIN)
with a subquery (there should be an adaptation from the shortest more optimized query in the cited answer, but let's see if this one works first):
SELECT t.*
FROM tickets t
INNER JOIN ( SELECT ticketnumber, MAX(tu.sequence) AS maxSequence
FROM ticket_updates tu
WHERE tu.type = 'update' AND concat('', tu.contact_name * 1) <> tu.contact_name
GROUP BY ticketnumber ) tu2
ON (t.ticketnumber = tu2.ticketnumber)
WHERE t.status <> 'Completed'
AND LOWER(t.department) = 'support';
see it in action

Related

#1222 - The used SELECT statements have a different number of columns problem [duplicate]

Why am i getting a #1222 - The used SELECT statements have a different number of columns
? i am trying to load wall posts from this users friends and his self.
SELECT u.id AS pid, b2.id AS id, b2.message AS message, b2.date AS date FROM
(
(
SELECT b.id AS id, b.pid AS pid, b.message AS message, b.date AS date FROM
wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10
)
UNION
(
SELECT * FROM
wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10
)
ORDER BY date DESC
LIMIT 0, 10
) AS b2
JOIN Users AS u
ON b2.pid = u.id
WHERE u.banned='0' AND u.email_activated='1'
ORDER BY date DESC
LIMIT 0, 10
The wall_posts table structure looks like id date privacy pid uid message
The Friends table structure looks like Fid id buddy_id invite_up_date status
pid stands for profile id. I am not really sure whats going on.
The first statement in the UNION returns four columns:
SELECT b.id AS id,
b.pid AS pid,
b.message AS message,
b.date AS date
FROM wall_posts AS b
The second one returns six, because the * expands to include all the columns from WALL_POSTS:
SELECT b.id,
b.date,
b.privacy,
b.pid.
b.uid message
FROM wall_posts AS b
The UNION and UNION ALL operators require that:
The same number of columns exist in all the statements that make up the UNION'd query
The data types have to match at each position/column
Use:
FROM ((SELECT b.id AS id,
b.pid AS pid,
b.message AS message,
b.date AS date
FROM wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10)
UNION
(SELECT id,
pid,
message,
date
FROM wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10))
You're taking the UNION of a 4-column relation (id, pid, message, and date) with a 6-column relation (* = the 6 columns of wall_posts). SQL doesn't let you do that.
(
SELECT b.id AS id, b.pid AS pid, b.message AS message, b.date AS date FROM
wall_posts AS b
JOIN Friends AS f ON f.id = b.pid
WHERE f.buddy_id = '1' AND f.status = 'b'
ORDER BY date DESC
LIMIT 0, 10
)
UNION
(
SELECT id, pid , message , date
FROM
wall_posts
WHERE pid = '1'
ORDER BY date DESC
LIMIT 0, 10
)
You were selecting 4 in the first query and 6 in the second, so match them up.
Beside from the answer given by #omg-ponies; I just want to add that this error also occur in variable assignment. In my case I used an insert; associated with that insert was a trigger. I mistakenly assign different number of fields to different number of variables. Below is my case details.
INSERT INTO tab1 (event, eventTypeID, fromDate, toDate, remarks)
-> SELECT event, eventTypeID,
-> fromDate, toDate, remarks FROM rrp group by trainingCode;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
So you see I got this error by issuing an insert statement instead of union statement. My case difference were
I issued a bulk insert sql
i.e. insert into tab1 (field, ...) as select field, ... from tab2
tab2 had an on insert trigger; this trigger basically decline duplicates
It turns out that I had an error in the trigger. I fetch record based on new input data and assigned them in incorrect number of variables.
DELIMITER ##
DROP TRIGGER trgInsertTrigger ##
CREATE TRIGGER trgInsertTrigger
BEFORE INSERT ON training
FOR EACH ROW
BEGIN
SET #recs = 0;
SET #trgID = 0;
SET #trgDescID = 0;
SET #trgDesc = '';
SET #district = '';
SET #msg = '';
SELECT COUNT(*), t.trainingID, td.trgDescID, td.trgDescName, t.trgDistrictID
INTO #recs, #trgID, #trgDescID, #proj, #trgDesc, #district
from training as t
left join trainingDistrict as tdist on t.trainingID = tdist.trainingID
left join trgDesc as td on t.trgDescID = td.trgDescID
WHERE
t.trgDescID = NEW.trgDescID
AND t.venue = NEW.venue
AND t.fromDate = NEW.fromDate
AND t.toDate = NEW.toDate
AND t.gender = NEW.gender
AND t.totalParticipants = NEW.totalParticipants
AND t.districtIDs = NEW.districtIDs;
IF #recs > 0 THEN
SET #msg = CONCAT('Error: Duplicate Training: previous ID ', CAST(#trgID AS CHAR CHARACTER SET utf8) COLLATE utf8_bin);
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = #msg;
END IF;
END ##
DELIMITER ;
As you can see i am fetching 5 fields but assigning them in 6 var. (My fault totally I forgot to delete the variable after editing.
You are using MySQL Union.
UNION is used to combine the result from multiple SELECT statements into a single result set.
The column names from the first SELECT statement are used as the column names for the results returned. Selected columns listed in corresponding positions of each SELECT statement should have the same data type. (For example, the first column selected by the first statement should have the same type as the first column selected by the other statements.)
Reference: MySQL Union
Your first select statement has 4 columns and second statement has 6 as you said wall_post has 6 column.
You should have same number of column and also in same order in both statement.
otherwise it shows error or wrong data.

Select something from mysql database and order it by count (where)

I have a problem with selecting something from my database. Here is the sql sentence:
SELECT name
FROM table1
JOIN table2
ON table1.id=table2.advid
GROUP BY advid
ORDER BY COUNT(table2.likes) ASC
This will output name with the least table2.likes to the highest value of table2.likes
The problem is that table2.likes contain both likes and dislikes. Likes are marked with 1, and dislikes are marked with 2 in the table.
Currently, if there is...
...written in the table, the syntax will count both likes and dislikes so the result would be 6. I would need this result to be zero, which means when counting, dislikes have to be deduced from the number of likes. Which also means this part of the sentence: ORDER BY COUNT(table2.likes) ASC would have to be changed, but I don't know how.
Use conditional aggregation with SUM():
SELECT name
FROM table1 t1 JOIN
table2 t2
ON t2.id = t2.advid
GROUP BY name
ORDER BY SUM(CASE WHEN t2.likes = 1 THEN 1 ELSE -1 END) ASC;
Note: I changed the GROUP BY to be by name. The GROUP BY columns should match the columns you are selecting.
Use a case expression to count 1 for likes and -1 for dislikes. It is considered good style and less error-prone not to join and then aggregate, but to join the already aggregated data instead.
select t1.name, t2.sumlikes
from table1 t1
join
(
select advid, sum(case when likes = 1 then 1 else -1 end) as sumlikes
from table2
group by advid
) t2 on t2.advid = t1.id
order by sumlikes;
If you want to list names without like entries, too, then turn the join into a left outer join and select coalesce(t2.sumlikes, 0) instead.

Use select result for WHERE clause

There is a table {id | date}. I want to select all rows where date > date from the given row.
Normally I would do it in two queries:
SELECT `date` FROM `mytable` WHERE `id`={id}; //get {date} from known {id}
and
SELECT * FROM `mytable` WHERE `date`> {date} //get the desired result
Is it possible to do it in one SQL query?
Thanks!
select * from mytable where date > (select date from mytable where id = {id})
This is a good candidate for a self-join.
SELECT o.*
FROM mytable t -- For every row from This table,
CROSS JOIN mytable o -- and for every row in the Other (see note #2) table
WHERE t.id <> o.id -- such that it is a different row
AND o.date > t.date -- with a date later
AND t.id = {id} -- than a specific row.
Notes:
Because > is used in the value comparison, the t.id <> o.id clause can be omitted. In any case, the query planner should figure this stuff out just fine.
The CROSS JOIN starts off a Cartesian product but the WHERE filters bring it back down quickly. In particular the t.id = {id} clause (assuming that id has a unique constraint) brings down the multiplicity to at most one output row for each input row in o.

MySQL Max statement not returning result

I have the following MySQL statement
SELECT * FROM user_messages AS T WHERE user_id = '33' AND id = (SELECT Max(id) from user_messages AS TT WHERE T.from_userid = TT.from_userid) ORDER BY status, id DESC
The problem I seem to be having is when I only have one record. I would think that MySQL would return the single record associated with user_link = '33', but instead it returns nothing.
I need to use the "Max" function because I use it to pull the most recent entries. I am trying to avoid having multiple queries or having to use php to sort also. Any help much appreciated!
This is your query:
SELECT *
FROM user_messages AS T
WHERE user_id = '33' AND
id = (SELECT Max(id)
from user_messages AS TT
WHERE T.from_userid = TT.from_userid
)
ORDER BY status, id DESC
Here are three reasons it could be failing to return any rows. First, user_id = '33' may not exist in the table. Second from_userid may be NULL. Third, the id value may be NULL for all matching records.
Perhaps this simpler version would help:
select *
from user_messages um
where user_id = '33'
order by id desc
limit 1
Thanks for your answer Gordon, I checked the database, and the record exists. I did some more research, and what it turns out to be is that I needed to join the data. I was able to return the Min or Max record, but the corresponding/related fields weren't returned with it.
SELECT * FROM user_messages INNER JOIN(SELECT from_username, MAX(id) AS id FROM user_messages WHERE user_link = '33' GROUP BY from_username ORDER BY status, id DESC) t2 ON user_messages.id = t2.id AND user_messages.from_username = t2.from_username
The thread that answered the question was this one - Need To Pull Most Recent Record By Timestamp Per Unique ID

What is faster in MySQL? WHERE sub request = 0 or IN list

I was wondering what is better in MySQL. I have a SELECT query that exclude every entry associated to a banned userID.
Currently I have a subquery clause in the WHERE statement that goes like
AND (SELECT COUNT(*)
FROM TheBlackListTable
WHERE userID = userList.ID
AND blackListedID = :userID2 ) = 0
Which will accept every userID not present in the TheBlackListTable
Would it be faster to retrieve first all Banned ID in a previous request and replace the previous clause by
AND creatorID NOT IN listOfBannedID
LEFT JOIN / IS NULL and NOT IN are fastest:
SELECT *
FROM mytable
WHERE id NOT IN
(
SELECT userId
FROM blacklist
WHERE blackListedID = :userID2
)
or
SELECT m.*
FROM mytable m
LEFT JOIN
blacklist b
ON b.userId = m.id
AND b.blackListedID = :userID2
WHERE b.userId IS NULL
NOT EXISTS yields the same plan but due to implementation flaws is marginally less efficient:
SELECT *
FROM mytable
WHERE NOT EXISTS
(
SELECT NULL
FROM blacklist b
WHERE b.userId = m.id
AND b.blacklistedId = :userID2
)
All these queries stop on the first match in blacklist (hence performing a semi-join)
The COUNT(*) solution is the least efficient, since MySQL will calculate the actual COUNT(*) rather than stopping on the first match.
However, if you have a UNIQUE index on (userId, blacklistedId), this is not much of problem as there cannot be more than one match anyway.
Use EXISTS clause to check for user not in blacklist.
Sample Query
Select * from userList
where not exists( Select 1 from TheBlackListTable where userID = userList.ID)
IN clause is used when there is fixed values or low count of values.