MySQL - Duplicate rows by different column - mysql

I have a table with the following structure:
id | user_id | job | type
The scenario is as following:
"A person in a company is offering his skills, another person is looking for skills. They can fill in their offerings and searches. After filling this in, they need to see the matches between what they are looking for and others are offering."
So "job" is a string, f.e. "creating backup","repairing bike"
So "type" is a string, f.e. "searching", "offering"
Is it possible to get these matches with one query?
** edit **
id | user_id | job | type
---|---------|---------------|----------
1 | 1 | Create backup | searching
2 | 1 | Format osx | searching
3 | 2 | Create backup | offering
4 | 1 | Program PHP | offering
I want to do a query SELECT * FROM table WHERE user_id = 1 AND type = 'offering' ... with a result that provides me an array of all the other users that are offering this. So that the user has a page with all the results of people that are offering the job that he is searching.

This will get you all user offering skills which are searching. Try it:
SELECT o.* FROM Table1 s
INNER JOIN Table1 o
ON s.job = o.job
WHERE s.type = 'searching'
AND o.type = 'offering'
AND s.user_id = (The user who is searching)

Related

Using nested SELECT result for IN statement of another nested SELECT

Be gentle. I'm a high school principal coding on the side for our school site.
I have looked at answers, here, here, and here. I might just not know enough to ask the right question.
We have events that have multiple sessions and there are workshops that can be associated with multiple sessions in multiple events.
I'm trying to get a csv result, later to be put into an array, for the associated sessions and events for my Workshops.
The query below works without the second nested Select statement.
In the Alt_Events statement, I need to pull the Event_IDs that are associated with the Session_IDs that are pulled from the first nested Select.
Events
ID | Name | Description
1 | Flex Learning | A day of flexible learning.
2 | Moonshot Expo | A day to join partners to solve problems.
Event_Sessions
ID | Event_ID | Name | Description
1 | 1 | Morning Session | The first session of the day.
2 | 1 | Afternoon Session | The afternoon session.
3 | 1 | Tutoring Session | A chance to get help from teachers.
4 | 2 | Partner Field Trip | The first session of the day.
5 | 2 | Brainstorming Session | The afternoon session.
6 | 2 | Tutoring Session | A chance to get help from teachers.
Event_Workshops
ID | Name | Description
1 | Math Tutorial | Get help from your math teachers.
Event_Workshop_Links
ID | Workshop_ID | Session_ID
1 | 1 | 3
2 | 1 | 6
Output Table:
ID | Name | Description | ... | Alt_Sessions | Alt_Events
1 | Math Tutorial | Get help... | ... | 3,6 | 1,2
Here is my query.
SELECT
ws.ID, ws.Name, ws.Description, ws.Location, ws.Owner_ID, ws.Max_Attendees,
ws.Eng_Major_Allowed, ws.Eng_Minor_Allowed,
ws.HC_Major_Allowed, ws.HC_Minor_Allowed,
ws.IT_Major_Allowed, ws.IT_Minor_Allowed,
u.LastName as Owner_LastName, u.FirstName AS Owner_FirstName, u.Email AS Owner_Email,
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN Alt_Sessions) AS Alt_Events
FROM Event_Workshops as ws
LEFT JOIN users AS u
ON ws.Owner_ID = u.ID
WHERE ws.ID = ?
ORDER BY ws.Name
I need to be able to pull the all event_ids that are in the Alt_Sessions result.
I'm guessing I can't use the result of the first nested query in the second nested query. If that's the problem, how can I pull that list of event ids?
Any and all help is greatly appreciated.
(Updated to show expected output. Also one error in transcribing the query. Session_ID instead of Event_ID in second nested statement.
Use the subquery instead of Alt_Sessions in the IN predicate like below.
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN (SELECT SESSION_ID FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID)) AS Alt_Events
Also, there is a way to make combinations of Alt_Sessions and Alt_Events first and then join to Event_Workshops.
SELECT * FROM Event_Workshops ws
JOIN
(
SELECT
wsl.Workshop_ID,
GROUP_CONCAT(wsl.Session_ID) Alt_Sessions,
GROUP_CONCAT(wsl.ID) Alt_Events
FROM Event_Workshop_Links wsl
GROUP BY wsl.Workshop_ID
) w
ON ws.ID = w.Workshop_ID

MySQL - How do I relate a custom table bi-directionally with user and usermeta tables? - Wordpress

I am using Wordpress, PhpMyAdmin, MySQL.
I am also quite novice when it comes to SQL and databases, so bear with me.
I have a Custom Data Table (this is a master table of user information), which I want to relate bi-directionally with the wp_users and wp_usermeta tables - meaning if a user updates their profile (Users or Usermeta tables) then the Custom Data Table would automatically update without me needing to manually input their updated information.
Likewise, if I edit the Custom Data Table I want the related information in Users and Usermeta to automatically update.
I tried creating a rough diagram of what is in my head. Diagram of data handling
The current system is terrible and very difficult to maintain. Another issue is that there are many SQL reports that use the current Custom Data Table. I understand that the solution I am looking for most likely means creating a completely new table. That's ok.
User
–––––––––––––––––––
| ID | username |
|–––––+–––––––––––|
| 376 | John |
| 377 | Paul |
| 378 | Ringo |
–––––––––––––––––––
Usermeta
–––––––––––––––––––––––––––––––––––––––––––––
| meta_id | user_id | meta_key | meta_value |
|–––––––––+–––––––––+––––––––––+––––––––––––|
| 8 | 1 | job | Keys |
| 102 | 2 | job | Vocals |
| 203 | 3 | job | Drums |
–––––––––––––––––––––––––––––––––––––––––––––
Custom_Table
––––––––––––––––––––––––––––––––––––––
| ID | username | job | user_ID |
|––––+–––––––––––+–––––––––+–––––––––|
| 1 | John | Keys | 376 |
| 2 | Paul | Vocals | 377 |
| 3 | Ringo | Drums | 378 |
––––––––––––––––––––––––––––––––––––––
Basically, this is what I have a query (except I have many more lines of the usermeta):
CREATE TABLE custom_table
SELECT users.username,
MAX(CASE WHEN meta_key = 'job' THEN meta_value ELSE NULL END) AS job,
users.ID
FROM wp_users as users, wp_usermeta as umeta
WHERE umeta.user_id = users.ID
This creates the table I want but if any of the tables are updated it is not 'synced' anywhere else. Is this possible? Would it be done by some sort of trigger (I am still unfamiliar with triggers)?
Just use a view instead of a table. This gives you an always up-to-date perspective at your actual data, without any maintenance cost (and, for what it worth, without storage cost either).
create view custom_view as
select
u.username,
um.meta_value as job
u.id as user_id
from wp_users as users u
left join wp_usermeta as umeta um on um.user_id = u.ID and um.meta_key = 'job'
Note about your original query:
it is actually invalid SQL (it was missing a group by clause)
event if it had a group by clause, it would filter out users without a job (which might not be what you want)
do not use old-school, implicit joins; always use standard joins (with the on keyword and no comma in the from clause)
Since you are interested in only one meta key per user, the query can be rewritten as a simple join, without aggregation.
You can use a Updateable VIEW to solve this:
CREATE VIEW custom AS
SELECT User.ID, User.username,
CASE WHEN meta_key = 'job' THEN meta_value ELSE NULL END AS job
FROM User INNER JOIN Usermeta ON Usermeta.user_id = User.ID
demo on dbfiddle.uk
For a view to be updatable, there must be a one-to-one relationship between the rows in the view and the rows in the underlying table. There are also certain other constructs that make a view nonupdatable.
In case you need one of the constructs you can only create the VIEW and have to update the source tables itself. The view is always synced with the source tables.

Removing Records with String Contained in Other Records using 3 tables and Joins

I previously got a great answer (thank you #Paul Spiegel) on removing records from a table whose string was contained at the end of another record. For example, removing 'Farm' when 'Animal Farm' existed) and grouped by a Client Field.
The problem is, in fact, a little more complex and spans three tables, I'd hoped I could extend the logic easily but it turns out to also be challenging (for me). Instead of one table with Client and Term, I have three tables:
Terms
Clients
Look-up-Table (LUT) where I store pairs of TermID and ClientID
I have made some progress since initially posting this question so where I stand is I made the Joins and resultant Select return the fields I want to delete from the Look-up-Table (LUT):
http://sqlfiddle.com/#!9/479c72/45
The final select being:
Select Distinct(C.Title),T2.Term From LUT L
Inner Join Terms T
On L.TermID=T.ID
Inner Join Terms T2
On T.Term Like Concat('% ', T2.Term)
Inner Join Clients C
On C.ID=L.ClientID;
I am in the process of trying to turn this into a Delete with little success.
Append this to your query:
Inner Join LUT L2
On L2.ClientID = L.ClientID
And L2.TermID = T2.ID
That will ensure, that the clients do match and you will get the following result:
| ClientID | TermID | ID | Term | ID | Term | ID | Title | ClientID | TermID |
|----------|--------|----|---------------|----|-----------|----|-------|----------|--------|
| 1 | 2 | 2 | Small Dog | 1 | Dog | 1 | Bob | 1 | 1 |
| 2 | 5 | 5 | Big Black Dog | 3 | Black Dog | 2 | Alice | 2 | 3 |
To delete the corresponding rows from the LUT table, replace Select * with Delete L2.
But deleting the terms is more tricky. Since it's a many-to-many relation, the term may belong to multiple clients. So you can't just delete them. You will need to cleanup up the table in a second statement. That can be done with the following statement:
Delete T
From Terms T
Left Join LUT L
On L.TermID = T.ID
Where L.TermID Is Null
Demo: http://sqlfiddle.com/#!9/b17659/1
Note that in this case the term Medium Dog will also be deleted, since it doesn't belong to any client.

Chaining results from multiple tables using SQL

I have a set of tables with following structures
**EntityFields**
fid | pid
1 | 1
2 | 1
3 | 2
4 | 2
5 | 1
**Language**
id | type | value
1 | Entity | FirstEntity
2 | Entity | SecondEntity
1 | Field | Name
2 | Field | Age
3 | Field | Name
4 | Field | Age
5 | Field | Location
Now as you may have understood, the first table gives the EntityField assignment to each Entity. The second table gives out the names for those IDs. What I want to output is something like the following
1 | FirstEntity / Name (i.e. a concat of the Entity and the EntityField name)
2 | FirstEntity / Age
3 | FirstEntity / Location
4 | SecondEntity / Name
5 | SecondEntity / Age
Is this possible?
Thank you for the answers, unfortunately the table structure is something that I cannot change. The table structure it self belongs to another data directory system which is quite flexible and which I am using to pull out data. I know that without providing the necessary background, this table structure looks quite weird, but it is something that works quite well (except in this scenario).
I will try out the examples here and will let you know.
For your current table structure, I think the following will work
SELECT EntityFields.fid, CONCAT(L1.value, ' / ' L2.value)
FROM EntityFields INNER JOIN Language as L1 ON EntityFields.pid=L1.id and L1.type='Entity'
INNER JOIN Language as L2 ON EntityFields.fid=L2.id and L2.type='Field'
ORDER BY EntityFields.fid
However, this query could be made much easier by having a better table structure. For example, with the following structure:
**EntityFields**
fid | pid | uid
1 | 1 | 1
2 | 1 | 2
1 | 2 | 3
2 | 2 | 4
3 | 1 | 5
**Entities**
id | value
1 | FirstEntity
2 | SecondEntity
**Fields**
id | value
1 | Name
2 | Age
3 | Location
you can use the somewhat simpler query:
SELECT uid, CONCAT(Entities.value, Fields.value)
FROM EntityFields INNER JOIN Entities ON EntityFields.pid=Entities.id
INNER JOIN Fields ON EntityFields.fid=Fields.id
ORDER BY uid
Well, I have no idea what you're trying to accomplish here. The fact that you label some records "Entity" and others "Field" and then try to connect them to each other makes it look to me like you are mixing two totally different things in the same table. Why not have an Entity table and a Field table?
You could get the results you seem to want by writing
select fid, le.value, lf.value
from entittyfields e
join language le on e.pid=le.id and type='Entity'
join language lf on e.fid=lf.id and type='Field'
order by fid
But I think you'd be wise to rethink your table design. Perhaps you could explain what you're trying to accomplish.
SELECT ef.fid AS id
, COALESCE(e.value, '-', ef.pid, ' / ', f.value)
AS entity_field
FROM EntityFields ef
JOIN Language AS e
ON e.id = ef.id
AND e.type = 'Entity'
JOIN Language AS f
ON f.id = ef.id
AND f.type = 'Field'
ORDER BY ef.pid
, ef.fid
If I understand your question, which I don't think I do, this is simple. It appears to be a set of very poorly designed tables (Language doing more than one thing, for example). And it appears that the Language table has two types of records: a) The Entity records, which have type='Entity' and b) Field records, which have type='Field'.
At any rate, the way I would approach it is to treat the Language table as if it were two tables:
select ef.fid, Entities.value, Fields.value
from entityfields ef
inner join language Entities
on Entities.id = ef.id
and Entities.type = 'Entity'
inner join language Fields
on Fields.id = ef.id
and Fields.Type = 'Field'
order by 2, 3
First stab, anyway. That should help you get the answer.

Finding shared list IDs in a MySQL table using bitwise operands

I want to find items in common from the "following_list" column in a table of users:
+----+--------------------+-------------------------------------+
| id | name | following_list |
+----+--------------------+-------------------------------------+
| 9 | User 1 | 26,6,12,10,21,24,19,16 |
| 10 | User 2 | 21,24 |
| 12 | User 3 | 9,20,21,26,30 |
| 16 | User 4 | 6,52,9,10 |
| 19 | User 5 | 9,10,6,24 |
| 21 | User 6 | 9,10,6,12 |
| 24 | User 7 | 9,10,6 |
| 46 | User 8 | 45 |
| 52 | User 9 | 10,12,16,21,19,20,18,17,23,25,24,22 |
+----+--------------------+-------------------------------------+
I was hoping to be able to sort by the number of matches for a given user id. For example, I want to match all users except #9 against #9 to see which of the IDs in the "following_list" column they have in common.
I found a way of doing this through the "SET" datatype and some bit trickery:
http://dev.mysql.com/tech-resources/articles/mysql-set-datatype.html#bits
However, I need to do this on an arbitrary list of IDs. I was hoping this could be done entirely through the database, but this is a little out of my league.
EDIT: Thanks for the help everybody. I'm still curious as to whether a bit-based approach could work, but the 3-table join works nicely.
SELECT a.following_id, COUNT( c.following_id ) AS matches
FROM following a
LEFT JOIN following b ON b.user_id = a.following_id
LEFT JOIN following c ON c.user_id = a.user_id
AND c.following_id = b.following_id
WHERE a.user_id = ?
GROUP BY a.following_id
Now I have to keep convincing myself not to prematurely optimize.
If you normalised your following_list column into a separate table with user_id and follower_id, then you'd find that COUNT() was extremely easy to use.
You'd also find the logic for selecting a list of followers, or a list of user's being followed much easier
Your problem would be simplified if you could split your following_list column off into a child table, e.g.
TABLE id_following_list:
id | following
--------------
10 | 21
10 | 24
46 | 45
...| ...
You can read more here.
Normalize the table, drop the column following_list, create a table following:
user_id
following_id
Which leads to the easy-peasy query (untested, you get the point):
SELECT b.user_id, COUNT(c.following)
FROM following a
JOIN following b -- get followings of <id>
ON b.following_id = a.following_id
AND b.user_id = a.following_id
JOIN following c -- get all (other) followings of <id> again, match with followings of b
ON b.following_id = c.following_id
AND c.user_id = a.user_id
WHERE a.user_id = <id>
GROUP BY b.user_id
ORDER BY COUNT(b.following) DESC
Performance may very well very based on indexes & size of dataset, maybe add a 'similarity' column which is updated at regular intervals or changes just for fast data retrieval.