Performing bidirectional join queries on tables with one to many relationship - mysql

I wanna have a bidirectional relationship between my two tables that are related by a one to many relationship.
So, i have a User table, with columns such as user_id, and transaction_id etc (and other user datails columns).
I have another table called transaction which has columns like transaction_id and user_id (to refer to the user this particular transaction belongs to).
Now, the issue is, a particular user can have multiple transactions attached to them, which means, this transaction table can have multiple rows for the same user. This makes it easy for me to get user details via join query. But I want to make this relationship bi-directional.
As in, if all i have is user details, i should be able to get the transaction details of that person. but since, a user can have multiple transactions at once, I'm not able to write a join query.
As such, I am yet to figure out how to store two transaction id's for a particular user, keeping in mind that i should be able to make a join query having the user details (I could always store transaction id's in the form of an array, but that won't let my join query happen).

Assuming this table structures :
transactions
id
user_id
tdate
amount
user
id
name
email
you seem to be already aware that you can list all transactions of a user, along with his information, with :
SELECT
u.id,
u.name,
u.email,
t.id,
t.tdate,
t.amount
FROM
users u
INNER JOIN transactions t ON t.user_id = u.id
WHERE
u.id = ?
ORDER BY
u.id,
t.tdate
The question mark represents the id of the user whose transactions you want to see. This returns one record for each transaction of the user.
If you are looking to, for a given transaction, list all other transactions of the same user, then one solution is to add another join to the query : basically, this would first retrieve the given transaction by id (t0), and then list all transactions by the same user (t).
SELECT
u.id,
u.name,
u.email,
t.id,
t.tdate,
t.amount
FROM
users u
INNER JOIN transactions t0 ON t0.user_id = u.id
INNER JOIN transactions t ON t.user_id = u.id
WHERE
t0.id = ?
ORDER BY
u.id,
t.tdate

Related

Finding Values of same ID from different table

I have 2 tables:
1.Users (This table contains all the information of users like name, Userid, mobileno)
2.Transaction (This table contains the information of all the transaction of a user)
But the UserID is same in both the tables
I have some filter conditions like:
[ TransactionType=1 AND status=1 and (RealCash>0 or Bonus>0 or Winning>0)] which i want to apply on Transaction table
once I applied the condition i will have some UserID
Now i want that the information of the users from the Users table that have the same UserID which i've obtained from above from the transaction table
How can i do that in MYSQL ?
use JOIN : https://www.mysqltutorial.org/mysql-join/
For example:
SELECT
u.name,
u.Userid,
u.mobileno,
t.TransactionType
FROM
Users u
INNER JOIN Transaction t ON t.Userid = c.Userid
WHERE t.TransactionType=1 AND t.status=1 and (t.RealCash>0 or t.Bonus>0 or t.Winning>0)
But read carefully about other join types (left, right, cross) as you may get different results.
SELECT
name, Userid, mobileno
FROM
Users
WHERE
UserID IN (SELECT
UserID
FROM
Transaction
WHERE
TransactionType=1 AND status=1 and (RealCash>0 or Bonus>0 or Winning>0);

how can I return multiple row counts

I have three entities, User, Rule, and Activity
with the following relationship, One rule has many activities (bidirectional)
Many users have many activities(bidirectional), the server is with spring using spring jpa
I want to show how many Activities has one user started but not finished from every rule, with SQL this query would return that from one rule
SELECT COUNT(*) FROM user_activity WHERE user_id=userId AND score<10 AND
activity_id IN (SELECT id FROM activity WHERE rule_id=ruleId);
I could do that with JPQL, but how could I return that count from every rule?
so I could have from every rule, how many activities has that User
if someone needs it I can provide java model code too.
tables are User, Activity, Rule and User_Activity
I think this query is probably what you want. It finds all the activities associated with a given rule, then joins the users participating in that activity who have not finished it (I presume that's what score<10 indicates?)
SELECT r.id, u.id, COUNT(a.id)
FROM rule r
JOIN activity a ON a.rule_id = r.id
JOIN user_activity ua ON ua.activity_id = a.id AND ua.score < 10
JOIN user u ON u.id = ua.user_id
GROUP BY r.id, u.id
Obviously you can expand the SELECT to include things like the rule description and the user name etc.
I couldn't see any indication of what "activity complete" means so I've presumed it's that the score is less than 10. If that's not the case, you would need to change the join to user_activity appropriately.

Pull multiple rows from one table but only one row from a related table

I currently am trying to join two tables but prevent duplication of information from one of the tables.
The user's table has 4 columns, uid, name, email and status.
The stats table has 4 columns, uid, date, follows, views
What I would like to be able to do is pull every record from the stats table and only the name, email and status values from the user table. The issue I have with the below SQL is that it duplicates the data from the user table, is there a way around this?
SELECT u.name
, u.email
, u.status
, s.date
, s.follows
, s.views
FROM users u
JOIN stats s
ON u.id = s.uid
WHERE name = :name
If you don't want every matching stats row to be accompanied by its matching users row, then you have to run two queries:
SELECT u.id, u.name, u.email, u.status FROM users u WHERE name = :name
Note the result of u.id because you'll use it as a parameter for the second query:
SELECT s.date, s.follows, s.views FROM stats s WHERE s.uid = :uid
You have to understand that the relational model works because every query result is itself a relation. The matching data is returned in every row, and this is what allows JOIN to be part of an algebra, where the result can be used as the operand of another JOIN, or a GROUP BY, or some other relational operation.
You should read SQL and Relational Theory: How to Write Accurate SQL Code by C. J. Date.

Querying a large table using mysql

I manage a property website. I have a table with banned users (small table) and a table called advert_views which keeps track of each listing that each user views (currently 1.3m lines and growing). The advert_views table alsio takes note of the IP address for every advert viewed).
I want to get the IP addresses used by the banned users and check if any of these banned users have opened new accounts. I ran the following query:
SELECT adviews.user_id AS 'banned user_id',
adviews.client_ip AS 'IPs used by banned users',
adviews2.user_id AS 'banned users that opened a new account'
FROM banned_users
LEFT JOIN users on users.email_address = banned_users.email_address #since I don't store the user_id in banned_users
LEFT JOIN advert_views adviews ON adviews.user_id = users.id AND adviews.user_id IS NOT NULL # users may view listings when not logged in but they have restricted access to the information on the listing
LEFT JOIN (SELECT client_ip,
user_id
FROM advert_views
WHERE user_id IS NOT NULL
) adviews2
ON adviews2.client_ip = adviews.client_ip
WHERE banned_users.rec_status = 1 and adviews.user_id <> adviews2.user_id
GROUP BY adviews2.user_id
I applied an index on the advert_views table and the users table as per below:
enter image description here
My query takes half an hour to execute. Is there a way how to improve my query speed?
Thanks!
Chris
First of all: Why do you outer join the tables? Or better: Why do you try to outer join the tables? A left join is meant to get data from a table even when there is no match. But then your results could contain rows with all values null. (That doesn't happen though, because adviews.user_id <> adviews2.user_id in your where clause dismisses all outer-joined rows.) Don't give the DBMS more work to do than necessary. If you want inner joins, then don't outer join. (Though the difference in execution time won't be huge.)
Next: You select from banned_users, but you only use it to check existence. You shouldn't do this. Use an EXISTS or IN clause instead. (This is mainly for readability and in order not to produce duplicate results. This probably won't speed things up.)
SELECT av1.user_id AS 'banned user_id',
av2.client_ip AS 'IPs used by banned users',
av2.user_id AS 'banned users that opened a new account'
FROM adviews av1
JOIN adviews av2 ON av2.client_ip = av1.client_ip AND av2.user_id <> av1.user_id
WHERE av1.user_id IN
(
SELECT user_id
FROM users
WHERE email_address IN (select email_address from banned_users where rec_status = 1)
)
GROUP BY av2.user_id;
You may replace the inner IN clause with a join. It's mostly a matter of personal preference, but it is also that in the past MySQL sometimes didn't perform well on IN clauses, so many people made it a habit to join instead.
WHERE av1.user_id IN
(
SELECT u.user_id
FROM users u
JOIN banned_users bu ON bu.email_address = u.email_address
WHERE bu.rec_status = 1
)
At last consider removing the GROUP BY clause. It reduces your results to one row per reusing user_id, showing one of its related banned user_ids (arbitrarily chosen in case there is more than one). I don't know your tables. Are you getting many records per reusing user_id? If not, remove the clause.
As to indexes I suggest:
banned_users(rec_status, email_address)
users(email_address, user_id)
adviews(user_id, client_ip)
adviews(client_ip, user_id)

Trouble writing a query for three tables

There are three tables:
events, users, and subscriptions
I am trying to query the database to extract some fields (see select statement).
Users table relationships:
Linked to subscriptions by user_id (not every user has a subscription_id)
Linked to events by user_id (not every user has an event)
The problem is that not every user has a corresponding entry in the subscriptions table. I would like to display all users that have an event - whether they have a subscription or not.
SELECT subscriptions.plan, subscriptions.created_at, subscriptions.expires_at, users.first_name, events.subdomain, events.heading
FROM users
LEFT OUTER JOIN subscriptions ON users.id = subscriptions.user_id
LEFT OUTER JOIN events ON users.id = events.user_id
UNION
SELECT subscriptions.plan, subscriptions.created_at, subscriptions.expires_at, users.first_name, events.subdomain, events.heading
FROM users
RIGHT OUTER JOIN subscriptions ON users.id = subscriptions.user_id
RIGHT OUTER JOIN events ON users.id = events.user_id
This query takes a while to run, but seems to yield double results for some events.
Currently it returns all users, and sometimes duplicate entries per event (which is not the case in the actual DB). Any help is appreciated!