Simplify table with repeated entries MYSQL - mysql

I have created a table as a union of two SELECT statements, say FRIENDS_AND_NEIGHBORS, and I want to remove the repetitions of the two, except that they do not coincide in all the fields.
Simplifying my case, I have a table called FRIENDS (that has pairs of users, and its link id) and a table for USER that includes ZIP_CODE, from which I get the NEIGHBORS section. I'm also fixing a reference user with USER_ID = #usr, and ZIP_CODE = #zip. Then I do the following.
CREATE TABLE FRIENDS_AND_NEIGHBORS
(SELECT
USER_ID AS FRND_ID, # Choose neighbors by zipcode.
ZIP_CODE AS FRND_ZIP, #
0 AS FRND_LINK # The reference for friendship comes later.
FROM USER
WHERE ZIP_CODE = #zip)
UNION
(SELECT
frd.FRIEND_ID AS FRND_ID,
usr.ZIP_CODE AS FRND_ZIP,
frd.LINK AS FRND_LINK
FROM FRIENDS frd
JOIN USER usr
ON frd.FRIEND_ID = usr.USER_ID
WHERE frd.USER_ID = #usr);
Then I may be counting some neighbor/friends twice, but they still differ in the FRND_LINK
column, as I gave it a zero because I couldn't join the two.
I want to remove the corresponding neighbor row that has 0, when it has been counted as a friend.
Thank you for your help.

Related

How do I write a query that includes users that aren't in the table at all?

I currently have this query set up:
SELECT age, hobbies, country
FROM profile
INNER JOIN matches
ON profiel.usernumber = matches.user1
WHERE age BETWEEN ? and ?
AND usernumber != ?
AND usernumber NOT IN (SELECT user2 FROM matches WHERE matches.user1 = ?)
FYI:
I'm using two tables.
Table "profile" has all the user info we need (age, hobbies, etc.).
Table "matches" has two user numbers of users that matched. Only if you click the "match" button you get inserted into the "matches" table with your user number and your match.
The problem:
The current query only selects users that are in the table "matches" but aren't matched with you. I also want the users that aren't matched at all (and thus aren't in the matches table)
I've tried multiple options with AND or OR but I haven't figured anything out myself. I'm new to coding so I'm grateful for any help.
INNER JOIN only show records that exists on both tables. Use LEFT JOIN instead.
SELECT age, hobbies, country
FROM profile
LEFT JOIN matches ON profiel.usernumber = matches.user1

MySQL query to find values in table with given value and find others that are similar

I have a table called followers and I want to be able to find the current users followers, display that list, and then compare those values against another user ID to see if they are following them or not from that same table
This is the table I'm using currently to get the list of followers for a specific user:
followers
-------
followId - primary key, the unique id for the follow relationship
userId - the user that is following someone
orgId - that someone that the user is following
I tried using a union query but I wouldn't want to due to performance reasons (table may contain high volume of records) and because it isn't scalable (I think)?
The expected output should be the list of orgId's for which the user(the user I am checking against) is following, and another column that shows whether my user(my userId that I provide) is following that orgId value (i.e a following column).
Hmmm, if I understand correctly, you have two users and you want to know which orgs followed by the first are also followed by the second:
select f.orgid,
(exists (select 1
from followers f2
where f2.userId = $seconduserid and
f2.orgid = f.orgid
)
) as seconduserflag
from followers f
where f.userId = $firstuserid

MySQL find matching set of rows

I have a cross-reference table that supplies the many-to-many relationship between users and user group tables. It contains two relevant columns: group_id and user_id (surprise, surprise!). When a user wants to create a new group, I want to first check if that set of users already exists as a group.
Essentially I would define the problem as "Given a set of user ids, find any set of rows that match the set of user ids and all share the same group id".
Edit:
I'm looking for the exact set of users, not interesting in seeing in the resultset groups that include those users in addition to other users.
Sample Data
I have the hunch that a subquery is the way to go, but I can't figure out how to arrange it. Any help would be greatly appreciated!
Is this what you want?
select groupid
from usergroups ug
where userid in ($user1, $user2, . . . , $usern)
group by groupid
having count(*) = <n>;
This returns all groups that have the supplied list of users.
If you want the exact set, then:
select groupid
from usergroups ug
group by groupid
having count(*) = sum( userid in ($user1, $user2, . . . , $usern) );
This assumes that groups don't have the same user twice (it is not hard to adjust for that, but the condition becomes more complicated).

JOINing tables while ignoring duplicates

So, let's say I have a hash/relational table that connects users, teams a user can join, and challenges in which teams participate (teams_users_challenges), as well as a table that stores entered data for all users in a given challenge (entry_data). I want to get the average scores for each user in the challenge (the average value per day in a given week). However, there is a chance that a user will somehow join more than one team erroneously (which shouldn't happen, but does on occasion). Here is the SQL query below that gets a particular user's score:
SELECT tuc.user_id, SUM(ed.data_value) / 7 as value
FROM teams_users_challenges tuc
LEFT JOIN entry_data ed ON (
tuc.user_id = ed.user_id AND
ed.entry_date BETWEEN '2013-09-16' AND '2013-09-22'
)
WHERE tuc.challenge_id = ___
AND tuc.user_id = ___
If a user has mistakenly joined more than one team, (s)he would have more than one entry in teams_users_challenges, which would essentially duplicate the data retrieved. So if a user is on 3 different teams for the same challenge, (s)he would have 3 entries in teams_users_challenges, which would multiply their average value by 3, thanks to the LEFT JOIN that automatically takes in all records, and not just one.
I've tried using GROUP BY, but that doesn't seem to restrict the data to only one instances within teams_users_challenges. Does anybody have any ideas as to how I could restrict the query to only take in one record within teams_users_challenges?
ADDENDUM: The columns within teams_users_challenges are team_id, user_id, and challenge_id.
If this is a new empty table, you can express your 'business rule' that a user should only join one team per challenge as a unique constraint in SQL:
alter table teams_users_challenges
add constraint oneUserPerTeamPerChallenge
unique (
user_id
, team_id
, challenge_id
);
If you can't change the table, you'll need to group by user and team and pick a single challenge from each group in the query result. Maybe pick just the latest challenge.
I can't test it, but if you can't clean up the data as Yawar suggested, try:
SELECT tuc.user_id, SUM(ed.data_value) / 7 as value
FROM entry_data ed
LEFT JOIN
(
select tuc.user_id, tuc.challenge_id from teams_users_challenges tuc group by tuc.user_id, tuc.challenge_id
) AS SINGLE_TEAM
ON SINGLE_TEAM.user_id = ed.user_id AND
ed.entry_date BETWEEN '2013-09-16' AND '2013-09-22'
WHERE tuc.challenge_id = ___
AND tuc.user_id = ___

mysql NOT IN query

I am trying to produce a list of user that have access to particular locations. The table in my database that records locations users have access to is backwards in my opinion. It list the location the users can not access. I have tried not in several different ways without any luck. The query just returns a blank results set. I've tried find something on here to help me and googled it but this has not helped...I always get blank results screen(nothing returned). I think the issue lies with the USER_LOCATION_EXCLUSION having several entries for each USER_ID for each corresponding location. I did not create this table structure but inherited it.
Scenario
Tables Columns
====================================================
APP_USER USER_ID, USER_NAME
LOCATION LOCATION_ID, LOCATION_NAME
USER_LOCATION_EXCLUSION USER_ID, LOCATION_ID
APP_USER and LOCATION tables both have IDs that are unique. These IDs are both used in USER_LOCATION_EXCLUSION (list locations users can not access) and can be used many times in this table according to the users access. I would like to produce report that has the USER_NAME and LOCATION_NAME they have access to.
What it sounds like you want is a Cartesian result LESS the exclusions... ie: for each user, assume giving them EVERY location access, then find out what they are excluded from...
select
PreQuery.User_ID,
PreQuery.User_Name,
PreQuery.Location_Name
from
( select
AU.User_ID,
AU.User_Name,
L.Location_ID,
L.Location_Name
from
App_User AU,
Location L ) PreQuery
LEFT JOIN USER_LOCATION_EXCLUSION ULE
on PreQuery.User_ID = ULE.User_ID
AND PreQuery.LOCATION_ID = ULE.LOCATION_ID
where
ULE.Location_ID = NULL
By doing a LEFT JOIN to the exclusion table, every record will ATTEMPT to be joined to the exclusion list... So, when it DOES find a match, that location ID will exist... when it does NOT exist, it will be null (satisfying your NOT Excluded from)
This also eliminates sub-select WHERE clause tested for every user / location
I would use something like this. To obtain all combinations of locations-users you could use a Select query with no joins:
Select *
From Location, App_User
but from here you have to exclude combinations of locations-users that are included in user_location_list table. This can solve your problem:
Select
Location.*,
App_User.*
From App_USer, Location
Where
Not Exists (select * from user_location_exclusion
where user_location_exclusion.user_id=app_user.user_id
and user_location_exclusion.location_id=location.location_id)
This shows every location for every user that has not been excluded.