Hi I have 2 table Offense table and User_jobs table
offense table:
crime_id |crime_type |casenumber|
---------+-----------+----------+
1 | 3 |1 |
2 | 3 |1 |
1 | 3 |2 |
12 | AA |2 |
user_jobs table:
casenumber |disposal_status |
-----------+----------------+
1 | yes |
1 | yes |
2 | no |
2 | no |
what i want is to count the number of rows with the same combination say crime_id=1 and crime_type= 3 but these must have a disposal status of yes in the user_jobs table.
i want to do this in mysql. pliz help
sorry but i am new to mysql. i now want to display the real names of those id not the id themselves.
the tables with these IDs are crime_category and Crime_type Crime_catgory
table:
category |crime_id |
-----------+----------------+
theft | 1 |
murder | 2 |
rape | 3 | 2 |
no |
Crime_type table:
Crime_type |id |
---------------+----------------+
administrative | yes |
criminal | yes |
You can do this with a simple inner join and an aggregate function:
select
o.crime_id,
o.crime_type,
count(*)
from
offence o
join user_jobs uj
on o.casenumber=uj.casenumber
where
uj.disposal_status='Yes'
group by
o.crime_id,
o.crime_type
This will pick up distinct combinations of the first two columns joined as they should tot he jobs table and only where the disposal_status is equal to 'Yes'
Edit: You would probably do really well to have a read of this Q&A that I put together for exactly this sort of situation - where I give you the code for it, but would like to explain this is a lot more detail. The Q&A explains why this type of thing (and many many others) work and how they do so:
How can an SQL query return data from multiple tables
Edit 2:
select
o.crime_id,
o.crime_type,
ct.category,
count(*)
from
offence o
join user_jobs uj
on o.casenumber=uj.casenumber
join crime_type ct
on o.crime_type=ct.crime_id
where
uj.disposal_status='Yes'
group by
o.crime_id,
o.crime_type,
ct.category,
Related
Hello :) I am fairly new to using INNER JOIN and still trying to comprehend it's logic which I think I am sort of beginning to understand. After being across a few different articles on the topic I have generated a query for finding duplicates in my table of phone numbers.
My table structure is as such:
+---------+-------+
| PhoneID | Phone |
+---------+-------+
Very simple. I created this query:
SELECT A.PhoneID, B.PhoneID FROM T_Phone A
INNER JOIN T_Phone B
ON A.Phone = B.Phone AND A.PhoneID < B.PhoneID
Which returns the ID of a phone that matches another one. I don't know how to word that properly so here is an example output:
+---------+---------+
| PhoneID | PhoneID |
+---------+---------+
| 17919 | 17969 |
| 17919 | 22206 |
| 17919 | 23837 |
| 17920 | 17970 |
| 17920 | 22203 |
| 17920 | 23834 |
| 17921 | 17971 |
| 17921 | 22225 |
| 17921 | 22465 |
| 17921 | 24011 |
| 17921 | 24047 |
| 17922 | 17972 |
| 17922 | 22198 |
| 17922 | 23879 |
| 17923 | 17973 |
| 17923 | 22199 |
| 17923 | 23880 |
+---------+---------+
You can note that on the left there is repeating IDs, the phone number that matches will be on the right (These are just the IDs of said numbers). what I am trying to accomplish, is to actually change a join table relative to the ID on the right. The join table structure is as such:
+----------+-----------+
| T_JoinID | T_PhoneID |
+----------+-----------+
Where T_JoinID is a larger object with a collection of those T_PhoneIDs, hence the join table. What I want to do is take a row from the original match query, and find the right side PhoneID in the join table, then update that item in the Join to be equal to the left side PhoneID. Repeating this for each row.
It's sort of a way to save space and get rid of matching numbers, I can just point the matching ones to the original and use that as a reference when I need to retrieve it.
After that I need to actually delete the original numbers that I reset the reference for but... This seems like a job for 2 or 3 different queries.
EDIT:
Sorry I know I didn't include enough detail. Here is some additional info:
My exact table structure is not the same as here but I am only using the columns that I listed so I didn't consider the fact that any of the others would matter. Most of the tables have a unique ID that is auto incremented. The phone table has carrier, type, ect columns. The additional columns I felt were irrelevant to include, but if there is a solution that includes the auto incremented ID of each table, let me know :) Anyway, I sort of found a solution, using multiple queries though I am still interested to learn and apply knowledge based on this question. So I have a that join table that I mentioned. It might look something like this for the expected results. There is a before and after table in one sorry for poor formatting.
+--------------------+---------+----------+---------+
| Join Table Results | | | |
+--------------------+---------+----------+---------+
| Before | | After | |
| Join | Table | Join | Table |
| PersonID | PhoneID | PersonID | PhoneID |
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 2 |
| 1 | 3 | 1 | 3 |
| 2 | 4 | 2 | 1 |
| 2 | 5 | 2 | 5 |
| 2 | 6 | 2 | 6 |
| 3 | 7 | 3 | 5 |
| 3 | 8 | 3 | 5 |
| 3 | 9 | 3 | 5 |
| 3 | 10 | 3 | 8 |
| 3 | 11 | 3 | 9 |
+--------------------+---------+----------+---------+
So you can see that in the before columns, 7, 8, and 9 would all be duplicate phone numbers in the PhoneID - PhoneID relationship table I posted originally. After the query I wanted to retrieve the duplicates using the PhoneID - PhoneID comparison and take the ones that match, to change the join table in a way that I have shown directly above. So 7, 8, 9 all turn to 5. Because 5 is the original number, and 7, 8, 9 coincidentally were duplicates of 5. So I am basically pointing all of them to 5, and then deleting what would have been 7, 8, 9 in my Phone table since they all have a new relationship to 5. Is this making sense? xD It sounds outrageous typing it out.
End Edit
How can I improve my query to accomplish this task? Is it possible using an UPDATE statement? I was also considering just looping through this output and updating each row individually but I had a hope to just use a single query to save time and code. Typing it out makes me feel a tad obnoxious but I had hope there was a solution out there!
Thank you to anyone in advance for taking your time to help me out :) I really appreciate it. If it sounds outlandish, let me know I will just use multiple queries.
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).
I have a table called facility.
Structure looks as follows:
id | name
---------
1 | Hotel
2 | Hospital
3 | medical shop
I have an other table which is taking data from the above table and keeping multiple values in one column. View looks like below:
id | facilities
---------------
1 | Hospital~~medical shop~~Hotel
2 | Hospital~~Hotel
3 | medical shop~~Hotel
If I want to join these two tables how does the query look like?
I tried this, but it didn't work:
select overview.facilities as facility
from overview join facility on facility.id=overview.facilities;
you can do this with a bit of hackery
select o.facilities as facility
from overview o
join facility f on find_in_set(f.facilities, replace(o.facilities, '~~', ','));
I would highly recommend you change the way you are storing data. currently it is considered un normalized and that quickly becomes a monster to deal with
you should change your table structure to look something more like this
+----------+--------------+
| facility |
+----------+--------------+
| id | name |
+----------+--------------+
| 1 | Hotel |
| 2 | Hospital |
| 3 | medical shop |
+----------+--------------+
+-----------+-------------+
| overview |
+-----------+-------------+
| id | facility_id |
+-----------+-------------+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 3 |
| 7 | 1 |
+-----------+-------------+
Code Explanation:
basically you are wanting to find the matching facilities in the overview. one handy function MySQL has is FIND_IN_SET() that allows you to find an item in a comma separated string aka find_in_set(25, '11,23,25,26) would return true and that matching row would be returned... you are separating your facilities with the delimiter ~~ which wont work with find_in_set... so I used REPLACE() to change the ~~ to a comma and then used that in the JOIN condition. you can go from here in multiple ways.. for instance lets say you want the facility id's for the overview.. you just add in the select GROUP_CONCAT(f.id) and you have all of the id's... note if you do that you need to add a GROUP BY at the end of your query to tell it how you want the results grouped
This question already has answers here:
Query to find posts with an exact set of tags (many-to-many relationship)
(2 answers)
Closed 9 years ago.
I have 3 tables:
users
+---------+
|id | name|
+---------+
|1 | Tom |
|2 | Adam|
+---------+
skills
+------------+
|id |name |
+------------+
|1 |baking |
|2 |driving|
|3 |etc |
+------------+
users_skills
+-----------------+
|user_id |skill_id|
+-----------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
+-----------------+
So in result I have two users:
Tom has 1 and 2 skill
Adam hass 1 and 2 and 3 skill
I want to get all users that have exactly skill 1 and 2, so I want to get Tom. How to achieve that?
If I wanted to get users with skill 1 OR skill 2 - it's not a problem, but AND it is :(
Basically I tried to join users with users_skills table, but I get something like this:
+-----------------------+
|user_id |name |sill_id|
+-----------------------+
|1 |Tom | 1 |
|1 |Tom | 2 |
+-----------------------+
etc
so I have two rows and I don't know how to achieve AND statement here.
Here is the solution. The tricky part is the fact that not only has to match those skills but also NOT match any out of them.
SELECT u.id, u.name, us.skill_id FROM users u
JOIN users_skills us ON u.id = us.user_id
WHERE u.id IN (
SELECT user_id FROM users_skills
WHERE user_id NOT IN (
SELECT user_id FROM users_skills
WHERE skill_id NOT IN (1, 2)
)
GROUP BY user_id
HAVING COUNT(DISTINCT skill_id) = 2
)
Fiddle here. Note I've also added additional users and skills to test all cases.
In short: You first get rid of the ones that have additional unwanted skills and then you get rid of the ones that have less than the needed skills.
Output:
| ID | NAME | SKILL_ID |
|----|------|----------|
| 1 | Tom | 1 |
| 1 | Tom | 2 |
You need the DISTINCT function (and GROUP BY):
SELECT DISTINCT(u.name)
FROM users AS u
JOIN users_skills AS us
ON u.id = us.user_id
WHERE us.skillID IN (1,2)
GROUP BY u.id
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.