I have two tables,
TABLE 1 has many of each client and campaign and is very large
TABLE 2 has only one of each client and campaign and is small.
So I want to get the lastest(highest ID) from TABLE 1 where it matches the client and campaign in TABLE 2 and only one of each.
I have tried MAX, and playing with the order by etc, but cant get it working....
The results I get are choosing the lowest ID from TABLE 1 (I want highest)
$result2 = mysql_query("SELECT table1.client,table1.campaign,table1.id
FROM table1
LEFT OUTER JOIN
table2
ON (table2.client = table1.client)
AND (table2.campaign = table1.campaign )
WHERE (table2.enabled != 'disabled')
group by campaign asc
order by client,campaign,id asc
");
Help needed....
SELECT * FROM table1
INNER JOIN
(
SELECT MAX(table1.id) AS id FROM table1
INNER JOIN table2 ON table2.client = table1.client AND table2.campaign=table1.campaign and table2.enabled != 'disabled'
GROUP BY table1.client, table1.campaign
) AS m ON m.id = table1.id
I think that's what you're asking for. For each combination of client and campaign that exists in each table, it will give you the highest ID in table 1.
Related
I read many threads about getting only the first row of a left join, but, for some reason, this does not work for me.
Here is my structure (simplified of course)
Feeds
id | title | content
----------------------
1 | Feed 1 | ...
Artists
artist_id | artist_name
-----------------------
1 | Artist 1
2 | Artist 2
feeds_artists
rel_id | artist_id | feed_id
----------------------------
1 | 1 | 1
2 | 2 | 1
...
Now i want to get the articles and join only the first Artist and I thought of something like this:
SELECT *
FROM feeds
LEFT JOIN feeds_artists ON wp_feeds.id = (
SELECT feeds_artists.feed_id FROM feeds_artists
WHERE feeds_artists.feed_id = feeds.id
LIMIT 1
)
WHERE feeds.id = '13815'
just to get only the first row of the feeds_artists, but already this does not work.
I can not use TOP because of my database and I can't group the results by feeds_artists.artist_id as i need to sort them by date (I got results by grouping them this way, but the results where not the newest)
Tried something with OUTER APPLY as well - no success as well.
To be honest i can not really imagine whats going on in those rows - probably the biggest reason why i cant get this to work.
SOLUTION:
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT artist_id
FROM feeds_artists fa
WHERE fa.feed_id = f.id
LIMIT 1
)
WHERE f.id = '13815'
If you can assume that artist IDs increment over time, then the MIN(artist_id) will be the earliest.
So try something like this (untested...)
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT
MIN(fa.artist_id) a_id
FROM feeds_artists fa
WHERE fa.feed_id = f.feed_id
) a
Version without subselect:
SELECT f.title,
f.content,
MIN(a.artist_name) artist_name
FROM feeds f
LEFT JOIN feeds_artists fa ON fa.feed_id = f.id
LEFT JOIN artists a ON fa.artist_id = a.artist_id
GROUP BY f.id
#Matt Dodges answer put me on the right track. Thanks again for all the answers, which helped a lot of guys in the mean time. Got it working like this:
SELECT *
FROM feeds f
LEFT JOIN artists a ON a.artist_id = (
SELECT artist_id
FROM feeds_artists fa
WHERE fa.feed_id = f.id
LIMIT 1
)
WHERE f.id = '13815'
based on several answers here, i found something that worked for me and i wanted to generalize and explain what's going on.
convert:
LEFT JOIN table2 t2 ON (t2.thing = t1.thing)
to:
LEFT JOIN table2 t2 ON (t2.p_key = (SELECT MIN(t2_.p_key)
FROM table2 t2_ WHERE (t2_.thing = t1.thing) LIMIT 1))
the condition that connects t1 and t2 is moved from the ON and into the inner query WHERE. the MIN(primary key) or LIMIT 1 makes sure that only 1 row is returned by the inner query.
after selecting one specific row we need to tell the ON which row it is. that's why the ON is comparing the primary key of the joined tabled.
you can play with the inner query (i.e. order+limit) but it must return one primary key of the desired row that will tell the ON the exact row to join.
Update - for MySQL 5.7+
another option relevant to MySQL 5.7+ is to use ANY_VALUE+GROUP BY. it will select an artist name that is not necessarily the first one.
SELECT feeds.*,ANY_VALUE(feeds_artists.name) artist_name
FROM feeds
LEFT JOIN feeds_artists ON feeds.id = feeds_artists.feed_id
GROUP BY feeds.id
more info about ANY_VALUE: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
I've used something else (I think better...) and want to share it:
I created a VIEW that has a "group" clause
CREATE VIEW vCountries AS SELECT * PROVINCES GROUP BY country_code
SELECT * FROM client INNER JOIN vCountries on client_province = province_id
I want to say yet, that I think that we need to do this solution BECAUSE WE DID SOMETHING WRONG IN THE ANALYSIS... at least in my case... but sometimes it's cheaper to do this that to redesign everything...
I hope it helps!
Here is my answer using the group by clause.
SELECT *
FROM feeds f
LEFT JOIN
(
SELECT artist_id, feed_id
FROM feeds_artists
GROUP BY artist_id, feed_id
) fa ON fa.feed_id = f.id
LEFT JOIN artists a ON a.artist_id = fa.artist_id
I want to give a more generalized answer. One that will handle any case when you want to select only the first item in a LEFT JOIN.
You can use a subquery that GROUP_CONCATS what you want (sorted, too!), then just split the GROUP_CONCAT'd result and take only its first item, like so...
LEFT JOIN Person ON Person.id = (
SELECT SUBSTRING_INDEX(
GROUP_CONCAT(FirstName ORDER BY FirstName DESC SEPARATOR "_" ), '_', 1)
) FROM Person
);
Since we have DESC as our ORDER BY option, this will return a Person id for someone like "Zack". If we wanted someone with the name like "Andy", we would change ORDER BY FirstName DESC to ORDER BY FirstName ASC.
This is nimble, as this places the power of ordering totally within your hands. But, after much testing, it will not scale well in a situation with lots of users and lots of data.
It is, however, useful in running data-intensive reports for admin.
For some database like DB2 and PostgreSQL, you have to use the key word LATERAL for specifying a sub query in the LEFT JOIN : (here, it's for DB2)
SELECT f.*, a.*
FROM feeds f
LEFT JOIN LATERAL
(
SELECT artist_id, feed_id
FROM feeds_artists sfa
WHERE sfa.feed_id = f.id
fetch first 1 rows only
) fa ON fa.feed_id = f.id
LEFT JOIN artists a ON a.artist_id = fa.artist_id
I know this is not a direct solution but as I've faced this and it's always a huge problem for me, and also using left join select etc. sometimes lead to a heavy process cost in database and server, I prefer doing this kind of left joins using array in php like this:
First get the data in range from second table and while you need just one row from second table, just save them with left join in-common column as key in result array.
SQL1:
$sql = SELECT artist_id FROM feeds_artists fa WHERE fa.feed_id {...RANGE...}
$res = $mysqli->query($sql);
if ($res->num_rows > 0) {
while ($row = $res->fetch_assoc()) {
$join_data[...$KEY...] = $row['artist_id'];
}
Then, get the base data and add detail of left join table from previous array while fetch them like this:
SQL2:
$sql = SELECT * FROM feeds f WHERE f.id {...RANGE...};
$res = $mysqli->query($sql);
if ($res->num_rows > 0) {
while ($row = $res->fetch_assoc()) {
$key = $row[in_common_col_value];
$row['EXTRA_DATA'] = $join_data[$key];
$final_data[] = $row;
}
Now, you'll have a $final_data array with desire extra data from $join_data array. this usually works good for date range data and like this.
How to count value from two tables and return the result in a single value.?
I had two tables named order and affiliate, like below:
order:
order_ID(primary key), order_name, Order_status, affiliate_ID (foreign key)
affiliate:
affiliate_ID (primary key),affiliate_name
Now, I want to count the orders based on affiliate name by comparing the affiliate_ID on order table and affiliate table.
I have tried it like this:
Query
SELECT COUNT(*)
FROM order o
AND (SELECT COUNT (affiliate_name) FROM affiliate a
WHERE a.affiliate_id = O.affilate_id) AS total
It returns an error.
I feel this is what you need
SELECT a.affiliate_ID, a.affiliate_name, COUNT(o.order_ID) as total
FROM order o
LEFT JOIN affiliate a ON o.affiliate_ID = a.affiliate_ID
GROUP BY o.affiliate_ID;
to add WHERE clause, see sample
SELECT a.affiliate_ID, a.affiliate_name, COUNT(o.order_ID) as total
FROM order o
LEFT JOIN affiliate a ON o.affiliate_ID = a.affiliate_ID
WHERE o.affiliate_ID = 3
GROUP BY o.affiliate_ID;
Not sure I understand your question correctly, but is this what you're looking for ?
SELECT *, COUNT(*) as total
FROM order o
JOIN affiliate a ON a.affiliate_id = o.affiliate_id
GROUP BY o.affiliate_id
Otherwise, provive sample & desired result example please.
My tables are like this:
Table 1 (students)
Table 2 (results)
I want to select all students from Table 1 students who have 4 results in the results table. I tried this query, but with no success:
SELECT *
FROM students
WHERE gender = 'm'
AND (SELECT COUNT( result ) AS count
FROM results
INNER JOIN students ON results.stuID = students.stuID
WHERE result !=0
) =4
ORDER BY rank ASC
You can rewrite your query by using join and HAVING clause to check the count for each student group ,This can be done without using the subquery which sometimes affects on performance
SELECT s.*,COUNT(*) AS count
FROM students s
INNER JOIN results r ON r.stuID = s.stuID
WHERE r.result !=0
GROUP BY s.stuID
HAVING count =4
ORDER BY s.rank ASC
um, that's a little convoluted.
the where clause should come after the subquery, and the subquery still needs to be JOINed back to the main query.
something like
SELECT * FROM students
INNER JOIN (SELECT COUNT(result),results.stuID as count FROM results WHERE result != 0) as result_count
ON result_count.stuID = students.stuID
WHERE result_count.count =4 AND students.gender = 'm'
ORDER BY rank ASC
You have to use alias for table also -
SELECT *
FROM students as a
WHERE gender = 'm'
AND (SELECT COUNT(result) AS count
FROM results as b
WHERE b.stuID = a.stuID AND
(result!=0 OR result IS NOT NULL OR result!='')
) = 4
ORDER BY rank ASC
Im trying to construct a query that goes over 3 tables and im COMPLETELY stumped ... my knowledge limit is basic 1 table query and i need some help before i stick my head in a blender.
I have the following query
SELECT * FROM internalrole WHERE introle = $imarole
Im fine with that part .. its the next thats getting me all stressed.
That query returns the following columns ( id, user_id, introle, proven, used )
What i then need to do is take the user_id from the results returned and use it to get the following
SELECT * FROM users WHERE id = user_id(from previous query) AND archive = 0 and status = 8
I need to put that into 1 query, but wait, theres more .... from the results there, i need to check if that user's 'id' is in the availability table, if it is, check the date ( column name is date ) and if it matches todays date, dont return that one user.
I need to put all that in one query :S ... i have NO IDEA how to do it, thinking about it makes my head shake ... If someone could help me out, i would be eternaly grateful.
Cheers,
Use INNER JOIN, which links tables to each other based on a common attribute (typically a primary - foreign key relationship)
say an attribute, 'id', links table1 and table2
SELECT t1.att1, t2.att2
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id --essentially, this links ids that are equal with each other together to make one large table row
To add more tables, just add more join clauses.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
LEFT JOIN availability a
ON ir.user_id = a.user_id
AND a.date = CURDATE()
WHERE ir.introle = $imarole
AND a.user_id IS NULL /* User does NOT exist in availability table w/ today's date */
EDIT: This second query is based on the comments below, asking to show only users who do exist in the availability table.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
INNER JOIN availability a
ON ir.user_id = a.user_id
WHERE ir.introle = $imarole
Hmm, maybe something like this
SELECT * FROM users WHERE id IN (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8;
A handy thing for me to remember is that tables are essentially arrays in SQL.
HTH!
Nested queries are your friend.
SELECT * FROM users WHERE id in (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8
Alternatively joins:
SELECT * FROM users INNER JOIN internalrole ON users.id = internalrole.user_id WHERE internalrole.user_id = $imarole AND users.archive = 0 and users.status = 8
I am making a cookie based favorite system and need to join data from two tables based on the unique user id stored in the cookie so I can tell what items that use has marked as favorites.
I know I need to do a JOIN but have not used them much and dont really have my head around them yet.
Existing query that selects the items from the db:
$query = mysql_query("SELECT *, UNIX_TIMESTAMP(`date`) AS `date` FROM songs WHERE date >= DATE_SUB( NOW( ) , INTERVAL 2 WEEK ) ORDER BY date DESC");
My favorites table is setup as: ID FAVORITE USERID where ID is the primary key, FAVORITE is the song ID from table songs and USERID is a hash stored in a cookie.
I need to join in all the rows from the favorites table where the USERID field matches the cookie hash variable.
I also need to gather the total number of rows in favorites that match the song id so I can display a count of the number of people who set the item as favorite so I can display how many people like it. But maybe need to do that as a separate query?
This should do it, I would imagine:
$user_id = intval($_COOKIE['user_id']);
$query = mysql_query(sprintf("
SELECT *
FROM songs s
INNER JOIN favorites f
ON f.favorite = s.id
WHERE f.userid = %s
", $user_id));
You should probably read up on the different types of joins.
And then to get the total amount of rows returned, you can just call mysql_num_rows on the result:
$favorite_song_count = mysql_num_rows($query);
EDIT: To select all songs but note which are favorited, you would do this:
$query = mysql_query(sprintf("
SELECT s.*, f.id as favorite_id
FROM songs s
LEFT JOIN favorites f
ON f.favorite = s.id AND f.userid = %s
", $user_id));
By switching it from an INNER JOIN to a LEFT JOIN we are selecting all songs even if they don't have a corresponding record in the favorites table. Any songs that are favorites of the user_id provided will have a non-NULL value for favorite_id.
You can have logical (and, or, ...) operators in join conditions:
select t1.*
from t1
join t2 on t1.id = t2.fid and t2.foo = 'blah'
If you are also querying the total number of times each song has been "favorited" then you need a group by construct also, like this way:
select *, count(f.id)
from songs as s
left join favorites as f on s.id = f.favorite and f.userid = <hash>
group by s.id