Mysql SELECT from T1 WHERE ID in T2 not present in T3 - php

OK, so this might sound complicated, let me explain... i've been banging my head on this for a while, and i'm stuck in a loop now... can't figure it out!
T1 is a simple alert data table. It's got theses headers :
| ID | TITLE | DATA |
---------------------------------
| 1 | Title1 | Text1 |
| 2 | Title2 | Text2 |
T2, is just a simple user table
| ID | Name |
-------------------
| 1 | Fred |
| 2 | Bill |
| 3 | Brad |
T3 is a link table between T1 and T2. Basically, the first time a user (T2.ID) views an alert (T1.ID) he hasn't viewed, his ID is added to this table so I know he's viewed it.
| ID | T1ID | T2ID |
--------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 3 | 1 |
| 7 | 3 | 3 |
I can tell user 1 has not viewed alert 2 nor has user 2 viewed alert 3. So next time they login, I should popup these alerts to them... So if i'm user 1, when I login my admin site, I want MySQL to tell me I havn't viewed alert 2, then UPDATE that table when I do.
As I sayed, T3 should build up everytime a user views an alert where his ID does not match an alert ID. I feel, i'm so damn close...
Simple enough, now how do I do it? I ain't going to post what doesn't work...
Please help...

The first thing, you want to start with the alert table as it is applicable to all uers. From there, doing a LEFT-JOIN to the already viewed table specifically for the login user ID in question AND the alert IDs are the same.
The where clause is looking ONLY for those entries that it CAN NOT find the corresponding login user ID as having viewed the alert. For those, get the alert's title and data.
After that, and presentation to the user, you can insert after the alerts have been viewed.
I would ensure the already viewed table has an index on both keys (T1ID, T2ID) to help optimize the join
SELECT
T1.Title,
T1.Data
from
YourAlertTable T1
LEFT JOIN AlreadyViewedTable T3
ON T3.T2ID = TheUserIDParameterWhoLoggedIn
AND T1.ID = T3.T1ID
where
T3.T2ID IS NULL

try :
Select * from t1 where id not in (select t3.t1id from t3 where t3.t2id = <loggedin userid>)

Related

Multiple SQL fetch and print html-code if two tables is true

I'm trying to achieve a PHP, SQL and HTML solution. This is probably commonly done.
I have coded a project overview system from scratch. The state right now is that I have a SQL table for USERS and another table for PROJECTS. It works great to sign in with each user and also to list all projects for all the users.
But to be able to track which users that are connected to each project I created a third SQL table which is called PROJECTMEMBERS. It contains the rows USERS_ID and PROJECTS_ID which are supposed to hold the ID from the user and the project to be able to track the projectmembers. I'm not sure if this is the best solution but found it in another thread and thought it made sense. But now..
I have issues to figure out how to access different SQL tables in the same mysqli_fetch() query. How is this possibly done to present the USER IMAGE in each project? The current fetch is used to present all the projects but I still want to show all projects, but with an image of the active users on the project on each.
I hope I was clear enough for anyone to understand. Looking forward to any help out there.
SQL STRUCTURE EXAMPLE:
tbl_USERS tbl_PROJECTS
| id | username | USER_IMG | etc | | id | PROJECTNAME | DATE | etc |
---------------------------------- ----------------------------------
| 1 | XXXX | XXXXXXXX | 1 | | 1 | AAAA | AAAA | 1 |
| 2 | YYYY | YYYYYYYY | 2 | | 2 | BBBB | BBBB | 3 |
| 3 | ZZZZ | ZZZZZZZZ | 1 | | 3 | CCCC | CCCC | 4 |
tbl_PROJECTMEMBERS
| USERS_ID | PROJECTS_ID |
-------------------------
| 1 | 1 |
| 1 | 5 |
| 2 | 1 |
How it should be presented where USER_IMG is fetched from the users table:
-------------------------------- --------------------------------
| PROJECTNAME | | PROJECTNAME |
| DATE | | DATE |
| | | |
| | | |
| USER_IMG, USER_IMG | | USER_IMG, USER_IMG |
-------------------------------- --------------------------------
--------------------------------
| PROJECTNAME |
| DATE |
| |
| |
| USER_IMG |
--------------------------------
As you have 3 table users and projects and user_projects and you are fetching only projects from projects table then you can add join the first query where you fetch the projects it could some like this
SELECT p.,user. FROM projects as p
INNER JOIN user_projects as up
ON up.project_id = p.id
INNER JOIN users
ON users.id = up.user_id
Above I have used multijoin where project table connected with user_project and user_project conned with users table and in the select query we have fetch project and users table.
In above query you might get query if same attribute get repate into the both table like (ambiguity) so you can just skip the error just by alise it
SELECT user.id AS 'User ID', project.id as 'Project ID', project.name as 'Project Name' from user tbl_USERS , project tbl_PROJECTS, userprojects tbl_PROJECTMEMBERS where user.id = userprojects.USERS_ID AND project.id = userprojects.PROJECT_ID
This would be your mysql query, it would fetch the user id as User ID, project Id as Project ID, and project name as Project Name.
You can access those variables in plain php and output anyway you like.
Something like this?
$query = "SELECT a.*, b.* FROM USERS a INNER JOIN PROJECTS b on a.userID = b.userID
RIGHT JOIN USER_PROJECTS c ON a.userID = c.userID
WHERE a.userID = $variable";
$result = $db->Execute($query)
foreach($result as $data){
if($data['userID'] == $data['projectID']){
//do stuff here
}
}

Can SELECT, SELECT COUNT and cross reference tables be handled by just one query?

I have a page that displays a list of projects. With each project is displayed the following data retrieved from a mysqli database:
Title
Subtitle
Description
Part number (1 of x)
The total number of photos associated with that project
A randomly selected photo from the project
A list of tags
Projects are displayed 6 per page using a pagination system
As this is based on an old project of mine, it was originally done with sloppy code (I was just learning and did not know any better) using many queries. Three, in fact, just for items 5-7, and those were contained within a while loop that worked with the pagination system. I'm now quite aware that this is not even close to being the right way to do business.
I am familiar with INNER JOIN and the use of subqueries, but I'm concerned that I may not be able to get all of this data using just one select query for the following reasons:
Items 1-4 are easy enough with a basic SELECT query, BUT...
Item 5 needs a SELECT COUNT AND...
Item 6 needs a basic SELECT query with an ORDER by RAND LIMIT 1 to
select one random photo out of all those associated with each project
(using FilesystemIterator is out of the question, because the photos
table has a column indicating 0 if a photo is inactive and 1 if it is
active)
Item 7 is selected from a cross reference table for the tags and
projects and a table containing the tag ID and names
Given that, I'm not certain if all this can (r even should for that matter) be done with just one query or if it will need more than one query. I have read repeatedly how it is worth a swat on the nose with a newspaper to nest one or more queries inside a while loop. I've even read that multiple queries is, in general, a bad idea.
So I'm stuck. I realize this is likely to sound too general, but I don't have any code that works, just the old code that uses 4 queries to do the job, 3 of which are nested in a while loop.
Database structure below.
Projects table:
+-------------+---------+----------+---------------+------+
| project_id | title | subtitle | description | part |
|---------------------------------------------------------|
| 1 | Chevy | Engine | Modify | 1 |
| 2 | Ford | Trans | Rebuild | 1 |
| 3 | Mopar | Diff | Swap | 1 |
+-------------+---------+----------+---------------+------+
Photos table:
+----------+------------+--------+
| photo_id | project_id | active |
|--------------------------------|
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
| 5 | 2 | 1 |
| 6 | 2 | 1 |
| 7 | 3 | 1 |
| 8 | 3 | 1 |
| 9 | 3 | 1 |
+----------+------------+--------+
Tags table:
+--------+------------------+
| tag_id | tag |
|---------------------------|
| 1 | classic |
| 2 | new car |
| 3 | truck |
| 4 | performance |
| 5 | easy |
| 6 | difficult |
| 7 | hard |
| 8 | oem |
| 9 | aftermarket |
+--------+------------------+
Tag/Project cross-reference table:
+------------+-----------+
| project_id | tag_id |
|------------------------|
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 2 |
| 2 | 5 |
| 3 | 6 |
| 3 | 9 |
+------------+-----------+
I'm not asking for the code to be written for me, but if what I'm asking makes sense, I'd sincerely appreciate a shove in the right direction. Often times I struggle with both the PHP and MySQLi manuals online, so if there's any way to break this down, then fantastic.
Thank you all so much.
You're able to do subqueries inside your SELECT clause, like this:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo
FROM projects as p
Now, for the list of tags, as it returns more than one row, you can't do a subquery and you should do one query for every project. Well, in fact you can if you return all the tags in some kind of concatenation, like a comma separated list: tag1,tag2,tag3... but I don't recommend this one time that you will need to explode the column value. Do it only if you have many many projects and the performance to retrieve the list of tags for each individual project is fairly low. If you really want, you can:
SELECT
p.title, p.subtitle, p.description, p.part,
(SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos,
(SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo,
(SELECT GROUP_CONCAT(tag SEPARATOR ', ') FROM tags WHERE tag_id in (SELECT tag_id FROM tagproject WHERE project_id = p.project_id)) as tags
FROM projects as p
As you said from item 1 to 4 you already have the solution.
Add to the same query a SQL_CALC_FOUND_ROWS instead of a SELECT COUNT to solve the item 5.
For the item 6 you can use a subquery or maybe a LEFT JOIN limiting to one result.
For the latest item you can also use a subquery joining all the tags in a single result (separated by comma for instance).

How to return the first value from groups in one database table, MySQL

I have a table called photos in a MySQL database that contains photo filenames and other related info. Structure as follows:
+----------+------------+-------------+
| photo_id | gallery_id | photo_fname |
|----------|------------|-------------|
| 1 | 1 | DSC2001 |
| 2 | 1 | DSC2002 |
| 3 | 1 | DSC2003 |
| 4 | 2 | DSC2004 |
| 5 | 2 | DSC2005 |
| 6 | 2 | DSC2006 |
| 7 | 3 | DSC2007 |
| 8 | 3 | DSC2008 |
| 9 | 3 | DSC2009 |
+----------+------------+-------------+
What I want to do is write a query that returns the first photo filename for each gallery, as follows:
+----------+------------+-------------+
| photo_id | gallery_id | photo_fname |
|----------|------------|-------------|
| 1 | 1 | DSC2001 |
| 4 | 2 | DSC2004 |
| 7 | 3 | DSC2007 |
+----------+------------+-------------+
I researched this until my headache overcame me last night and although I found similar questions, they didn't seem to me to be looking to do the exact same thing. I tried subqueries, I tried the sample code from other posts here but couldn't make it work. Either due to brain fog or just not finding the right solution.
I just want to return the results as shown above, nothing more.
Many thanks in advance!
UPDATE:
My apologies, the table name is photos, FWIW.
Secondly, all I really want is the filename, not the photo or gallery Id, I just included those for visual clarity. I apologize if that caused confusion.
Try this:
SELECT *
FROM YourTable
WHERE photo_id IN ( SELECT MIN(photo_id)
FROM YourTable
GROUP BY gallery_id)
See a demo
For your updated question:
SELECT photo_fname
FROM photos
WHERE photo_id IN ( SELECT MIN(photo_id)
FROM photos
GROUP BY gallery_id)
Here is an easy way using inner join
SELECT
m.*
FROM sparkles as m
INNER JOIN (SELECT
photo_id,
MIN(photo_id)
FROM sparkles
GROUP BY gallery_id) as l
on l.photo_id = m.photo_id
Demo
SELECT photo_id, gallery_id, photo_frame
FROM ...
GROUP BY photo_id, gallery_id
by default, mysql will return the FIRST non-grouped value it encounters in the DB for any non-aggregated fields in the field list.
Select * from tablename group by gallery_id; ?

Compare two MySQL tables to filter results

I have a MySQL database with the following structure:
Table1
UniqueID (int) (PrimaryKey & AutoIncrement)
TitleID (varchar)
DescriptionID (varchar)
ContentRating (varchar)
Table2
UID (int) (PrimaryKey & AutoIncrement)
Activity (varchar)
ContentLimit (varchar)
What I want to do is take the value from ContentLimit (in Table2) and compare it against (ContentRating) in Table1, if they match then show all the rows that match. I'm using PHP and MySQL to achieve this.
Below is an Example:
Table1
UniqueID | TitleID | DescriptionID | ContentRating
------------------------------------------------------
1 | Hello | I Am Text | Universal
2 | Again | Yet More Text | Universal
3 | This | Yet More Text | Universal
4 | Is | Yet More Text | Parental Guidance
5 | Some | Yet More Text | Universal
6 | Dummy | Yet More Text | Parental Guidance
7 | Text | Yet More Text | Parental Guidance
8 | I | Yet More Text | Parental Guidance
9 | Think | Yet More Text | Parental Guidance
Table2
UID | Name | Activity | ContentLimit
---------------------------------------------
1 | John Smith | IsActive | Universal
2 | Jane Smith | IsActive | Universal
3 | Felix Tiger | IsActive | Parental Guidance
4 | Spring Load | InActive | Universal
If "Felix Tiger" was logged in then he would be able to see anything submitted with a "Parental Guidance" rating as well as anything with a "Universal" Rating.
But If "Jane Smith" was logged in then she would only be able to view anything submitted with a "Universal" rating
I apologise if I am unclear and will make clear anything that may be mis-read or hard to understand.
Thank you in advance for any help given.
Try this:
SELECT
a.*,
b.*
FROM table1 a
JOIN table2 b ON a.ContentRating = b.ContentLimit

query to imlement block list

I have two tables a and b as follows to implement a simple block list where users can block other users.....
Table A
+------------+--------------+------+
| Name | phone |userid|
+------------+--------------+------+
| Mr Sasi | 01225 708225 | 1 |
| Miss Brown | 01225 899360 | 2 |
| Mr Black | 01380 724040 | 3 |
+------------+--------------+------+
Table B
+------------+--------------+
| blockedbyid| blockedid |
+------------+--------------+
| 1 | 2 |
| 2 | 3 |
| 1 | 3 |
+------------+--------------+
"blockedbyid" is id of user who has blocked the user in "blockedid".
I need to join the two tables and fetch all records from table A such that the result has all users who are not blocked by a particular user [ie blockedbyid='XXX'].. Can you guys give the SQL query so that i can fetch the records as a recordset??? I dont want to fetch two different rowsets and compare it in php....
Something like this should work
Parameter :USERID
SELECT * FROM TABLEA WHERE userid NOT IN (SELECT blockedid FROM TABLEB WHERE blockedbyid = :USERID)
Using join
SELECT u.* FROM TABLEB b, TABLEA u WHERE b.blockedbyid = 'XXX' AND b.blockedid = NULL
It may work like that, give it a try.
Roadie57 solutions seems better though.

Categories