MySQL sub-query AND 'NOT IN' - mysql

sorry if this has been answered before; I'm a little unsure how best to describe this problem never mind search for it. But here goes...
Basically I have a 'projects' table, which holds and 'id' and a 'title'. I also have a 'projects_history' table which holds information when (IF) the project is set to archived (a boolean value). It has a 'pid' key that references the project - there can be more than one record for each project as it is updated (in order to track who sets the value to what).
There's also a 'project_enquiries' table that holds information on enquiries that have been raised for the project, so there is a 'pid' key that references 'projects'. Similarly, there's a 'project_enquiry_history' table that records when (IF) the enquiry is set to closed (a boolean value). It has a 'eid' key that references the project_enquiry - there can be more than one record for each enquiry as it is updated (in order to track who sets the value to what).
My query aims to pull out the projects that haven't been archived (so either there is no record in 'project_history' or the most recent record for the project has 'archived' = 0), which have enquiries that are still open (so either there is no record in 'project_enquiry_history' or the most recent record for the enquiry has 'open' = 1).
I'm really struggling on where to start with the query.

The first thing is that if there is no record in project_history, then you will need to do a left join of your projects table on the project_history table using your project id to discover this. You will have entries in the results that are null in the columns for project_history where there is no corresponding entry. You can then chain that process, using the result to do another left join on the 'project_enquiry_history' to find the items that have no entries.
You can also use these results as normal, to do selects of the data.
It will look something like:
SELECT * from project p LEFT OUTER JOIN project_history ph ON P.ID=ph.pid
LEFT OUTER JOIN project_enquiry_history peh ON peh.ID=ph.pid
WHERE (archived is NULL OR archived=0)
AND (open is NULL OR open=1)
You may have to tweak this to find the most recent if you are using timestamps and such.

Related

Should I save the same value(id) into tables or join them to retrive the value(id)?

Let's say I have a table posts that contains the User_id of the user who posted and the post post_id of the post. And I have another table comments that contains only the post that it belongs to child_of_post.
The problem is here: I need to select only from comments but at the same time get the user_id of the post that the comment belongs to.
So should I use a join like:
SELECT user_id FROM comments INNER JOIN posts ON child_of_post = post_id
Reading this confused me even more, I don't really know how to explain it, but, in general if I need to use the same value like and id, should I save that value in every table that I need it ? Or should I save it only in one table and use joins to retrieve it ?
Is using a join better that adding one more column to a table ?
Is using a join better than adding one more column to a table ?
In general : Yes.
Your database design looks good. As a general principle, avoid duplicating data across tables. This is inefficient in terms of storage, and also can quickly turn into a maintenance nightmare when it comes to modifying data, which ultimately threatens the integrity of your data.
Instead of duplicating data, the usual approach is to store a reference to the table row where the original data is stored ; this is called a foreign key, and it offers various functionalities that help maintain data integrity (prevent inserts of orphan records in the child table, delete child records when the parent is deleted, ...).
In your use case, you indeed would need to JOIN to recover the user that created the original post, like :
SELECT p.user_id, c.*
FROM comments c
INNNER JOIN posts p ON c.child_of_post = p.post_id
Assuming that post_id is the primary key of table posts, such JOIN with an equality condition referencing the primary key of another table, is very efficient, especially if you create an index on referencing column comments.child_of_post.
PS : it is a good practice to give aliases to table names and use them to index the fields in the query ; it avoids subtle bugs caused by column name clashes (when both tables have fields with the same name), and makes the query easier.

mysql query using column values as parameter in query phpMyAdmin

I have a query i have been working on trying to get a specific set of data, join the comments in duplicate phone numbers of said data, then join separate tables based on a common field "entry_id" which also happens to be the number on the end of the word custom_ to pull up that table.
table named list and tables containing the values i want to join is custom_entry_id (with entry_id being a field in list in which i need the values of each record to replace the words in order to pull up that specific table) i need entry_id from the beginning part of my query to stick onto the end of the word custom for every value my search returns to get the fields from that custom table designated for that record. so it will have to do some sort of loop i guess? sorry like i said I am at a loss at this point
this is where i am so far:
SELECT * ,
group_concat(comments SEPARATOR '\r\n\r\n') AS comments_combined
FROM list WHERE `status` IN ("SALEA","SALE")
GROUP BY phone_number
//entry_id is included in the * as well as status
// group concat combines the comments if numbers are same
i have also experimented on test data with doing a full outer join which doesnt really exist. i feel if you can solve the other part for me i can do the joining of the data with a query similar to this.
SELECT * FROM test
LEFT JOIN custom_sally ON test.num = custom_sally.num
UNION
SELECT * FROM test
RIGHT JOIN custom_sally ON test.num = custom_sally.num
i would like all of this to appear with every field from my list table in addition to all the fields in the custom_'entry_id' tables for each specific record. I am ok with values being null for records that have different custom fields. so if record 1 has custom fields after the join of hats and trousers and record 2 has socks and shoes i realize that socks and shoes for record 1 will be null and hats and trousers for record 2 will be null.
i am doing all this in phpmyadmin under the SQL tab.
if that is a mistake please advise as well. i am using it because ive only been working with SQl for a few months. from what i read its the rookie tool.
i might be going about this all wrong if so please advise
an example
i query list with my query i get 20,000 rows with columns like status, phone_number, comments, entry_id, name, address, so on.
now i want to join this query with custom fields in another table.
the problem is the custom tables' names are all linked to the entry_id.
so if entry_id is 777 then the custom table fields are custom_777
my database has over 100 custom tables with specials fields for each record depending on its entry_id.
when i query the records I don't know how to join the custom fields that are entry_id specific to the rest of my data.i will pull up some tables and data for a better example
this is the list table:
this is the custom_"entry_id"
Full Outer Join in MySQL
for info on full outer joins.

mySQL: join not finding data, even though they exist

I have a couple of tables in a mySQL database. For simplicity I'll just show some basic fields:
Table: sources:
sourceID int not null unique primary key
trigger int not null
<other stuff>
Table: sourceBS
id not null unique primary key
sourceID int not null,
name varchar(20),
SourceID in the in the sourceBS table is a foreign key referencing its namesake in sources, with the cascade option. I have tested this: if I delete an entry in sources, the corresponding entry in sourceBS also vanishes. Good.
I want to select some stuff from a join of sources and sourceBS, filtering based on a "sources" property. This should be easy, via a join which, I think, the foreign key should render pretty efficient, so:
SELECT sources.sourceID, sourceBS.*
FROM sources
LEFT JOIN sourceBS ON sources.sourceID = sourceBS.sourceID
WHERE trigger=1;
But when this runs, each row has "NULL" for the values returned from sourceBS, even sourceBS contains entries matching the condition. I can verify this:
SELECT *
FROM sourceBS
WHERE sourceID IN (
SELECT sourceID
FROM sources
WHERE trigger=1
);
Here I get a proper set of results, i.e. non-null values. But, while this works as a proof of concept, it's no good in real life because I want to return a bunch of stuff from the "sources" table as well, and I don't want to have to run multiple queries in order to get what I want.
Returning to the join, if I replace the left join with an inner join, then no results are returned. It is as if, somehow, the "join" is simply not finding any matches in the sourceBS table, and yet they are there as the second query shows.
Why is this happening? I know that this join has a 1:M relationship, sourceBS could have multiple entries for a given entry in sources, but that should be OK. I can test exactly this type of join on other DBs, and it works.
OK, so I've solved this - it wasn't a transaction issue in the end:when I tried it on the original machine, it failed again. It was the order of the join. It appears that in my terminal I had the "ON" clause the other way round to above, that is, I was doing:
... LEFT JOIN sourceBS ON (sourceBS.blockSourceID=sources.sourceID)
which returns all the nulls. If I do it (as in the above code I pasted)
... LEFT JOIN sourceBS ON (sources.sourceID=sourceBS.sourceID
it works. When I tried it the second time last night on a new machine, I'd used the second formulation.
Guess I'd better read up on joins to understand why this happened!

MySQL join 2 tables on non-unique column and with timestamp conditions

I have 2 MySQL Tables: "parts_revisions" and "categories_revisions". My goal is to use the revisions data in these tables to create a log that lists out all the changes made to parts and categories. Listing the changes to "parts" in one single SQL statement has proven tricky though! Here is the situation:
All entries of each table have "timestamp" columns.
Every parts_revisions entry has a "categoryId" that basically links it to the categories_revisions table. (Every part is a child of a parent category.)
All I want to do is list out all the parts_revisions, but use the human-friendly "name" column from the categories_revisions table based on the categoryId column in parts_revisions. This will make the log more readable.
The trick is that, because there are usually multiple revisions for each category within the categories_revisions table, I cannot do just one big 'ol join on the categoryId column to get the name. The categoryId column is non-unique, and "name"s may vary. What I have to do is get the latest category_revisions entry that has a timestamp that is no later than the timestamp of the part_revisions entry. In other words, we want to get the appropriate category name that was in use AT THE TIME the part revision was made.
Not sure if this matches your table structure, but here's a go at it. It's a bit of an ugly subquery inside a subquery. Guessing it won't be terribly efficient
select part_name,
category,
(select name
from categories_revisions
where categories_revisions.match_id = parts_revisions.category
and categories_revisions.timestamp = (select MAX(categories_revisions.timestamp)
from categories_revisions
where categories_revisions.match_id = parts_revisions.category
and categories_revisions.timestamp < parts_revisions.timestamp)) as name
from parts_revisions;
http://sqlfiddle.com/#!2/da74e/1/0

SQL Query to populate table based on PK of Main Table being joined

Here is my Database structure (basic relations):
I'm attempting to formulate a one-line query that will populate the clients_ID, Job_id, tech_id, & Part_id and return back all the work orders present. Nothing more nothing less.
Thus far I've struggled to generate this Query:
SELECT cli.client_name, tech.tech_name, job.Job_Name, w.wo_id, w.time_started, w.part_id, w.job_id, w.tech_id, w.clients_id, part.Part_name
FROM work_orders as w, technicians as tech, clients as cli, job_types as job, parts_list as part
LEFT JOIN technicians as techy ON tech_id = techy.tech_name
LEFT JOIN parts_list party ON part.part_id = party.Part_Name
LEFT JOIN job_types joby ON job_id = joby.Job_Name
LEFT JOIN clients cliy ON clients_id = cliy.client_name
Apparently, once all the joining happens it does not even populate the correct foreign key values according to their reference.
[some values came out as the actual foreign key id, not even
corresponding value.]
It just goes on about 20-30 times depending on largest row of a table that I have (one of the above).
I only have two work orders created, So ideally it should return just TWO Records, and columns, and fields with correct information. What could I be doing wrong? Haven't been with MySQL too long but am learning as much as I can.
Your join conditions are wrong. Join on tech_id = tech_id, not tech_id = tech_name. Looks like you do this for all your joins, so they all need to be fixed.
I really don't follow the text of your question, so I am basing my answer solely on your query.
Edit
Replying to your comment here. You said you want to "load up" the tech name column. I assume you mean you want tech name to be part of your result set.
The SELECT part of the query is what determines the columns that are in the result set. As long as the table where the column lives is referenced in the FROM/JOIN clauses, you can SELECT any column from that table.
Think of a JOIN statement as a way to "look up" a value in one table based on a value in another table. This is a very simplified definition, but it's a good way to start thinking about it. You want tech name in your result set, so you look it up in the Technicians table, which is where it lives. However, you want to look it up by a value that you have in the Work Orders table. The key (which is actually called a foreign key) that you have in the Work Orders table that relates it to the Technicians table is the tech_id. You use the tech_id to look up the related row in the Technicians table, and by doing so can include any column in that table in your result set.