Left join with on a limited right table - sql

I have cards, schedules, users. Each schedule has foreign keys to a user and a card. Assuming I have 100 cards and 2 users, each with 10 schedules. I would like a set of 100 cards joined with the 10 schedules of only 1 of the users. When I try to limit my dataset to 1 of the users, I only end up with 10 rows. How can I set up a query to achieve what I want?

Basically you want something in the form of:
select *
from cards c
left join
(
select *
from schedules s
join users u on s.UserID = u.ID
where u.ID = 1
) x on c.ID = x.CardID
So that your where clause doesn't interfere with the left join. You'll have to change the column names to whatever you are using, and possibly explicitly list the columns in the sub-query so there isn't any issues with overlapping column names.

Related

IS it possible to inner join 3 tables for SQL?

I'm trying to fetch certain data by using a column that exists in all 3 tables, I have three tables
[Txn].[TxnPaymentResponse]
[Txn].[Txn]
[Txn].[TxnLineItem]
Right now if I run the query, I'm able to get the correct data only userID.
But I want to grab another piece of information (suppose that call column X) from the 3rd table (TxnLineItem). That column doesn't exist in the first 2 tables. In this scenario how could I perform inner join and show that piece of info in the query?
DECLARE #CompletedTransactionSince DATETIME2(7) = '2022-09-13 00:00:00.000000'
SELECT DISTINCT
t.UserID
FROM
[Txn].[Txn] T WITH(NOLOCK)
INNER JOIN
[Txn].[TxnPaymentResponse] TPR WITH(NOLOCK) ON T.[TxnID] = TPR.[TxnID]
WHERE
TPR.[PaymentResponseType] = 'FINAL'
AND TPR.[AuthorizedAmount] > CONVERT(DECIMAL(9,3), 0)
AND (#CompletedTransactionSince IS NULL OR
T.[CreatedOn] > #CompletedTransactionSince)
Results from my query:
UserID
C1671FDA-70A8-4C07-BBDF-ACD06ADD145F
Table 3:
TxnID
StandardProductCategory
6FE0D0D0-9959-41AA-9BF0-00000003DED8
Carwash
D1B0EA51-C476-488C-A140-0000C1C7D099
General
Suppose I'm doing inner join like
INNER JOIN
[Txn].[TxnLineItem] TXL WITH(NOLOCK) ON T.[TxnID] = [Txn].[TxnID],
But my I want to grab the X column that has the same transactionID. I want to display UserID that has a Carwash only. Not sure if it's possible to write another clause with an inner join.
As per "I want to display UserID that has a Carwash only", you need to exclude records.
Just add these EXISTS and NOT EXISTS clauses at the end of to query.
EXISTS part is filter users that has CarWash.
NOT EXISTS part is exclude users that has StandardProduct Category records other than Car Wash
......
AND (#CompletedTransactionSince IS NULL OR
T.[CreatedOn] > #CompletedTransactionSince)
AND EXISTS ( SELECT 1 FROM [Txn].[TxnLineItem] TXL
WHERE T.[TxnID] = [Txn].[TxnID]
AND TXL.StandardProductCategory='Carwash')
AND NOT EXISTS( SELECT 1 FROM [Txn].[TxnLineItem] TXL
WHERE T.[TxnID] = [Txn].[TxnID]
AND TXL.StandardProductCategory=<>'Carwash')

Query records in one table that exists in either of two columns in another table

I have two tables. One with user info, one with payment info. I would like to find out users that are either the sender or the receiver of a payment.
Eample data:
user
id
other columns
1
2
3
payments:
sender
receiver
other columns
1
4
1
3
5
3
4
5
ideal output
id
1
3
what I tried:
SELECT id
FROM user u
where exists
(
SELECT 1
FROM payments p
where u.id = p.sender or u.id = p.receiver
)
BigQuery gave the error:
LEFT SEMI JOIN cannot be used without a condition that is an equality of fields from both sides of the join
which is quite confusing to me
It's because WHERE u.id = p.sender or u.id = p.receiver makes LEFT SEMI JOIN to be non-equi join.
You can separate WHERE condition into 2 EXITS clauses.
SELECT id
FROM user u
WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.sender)
OR EXISTS (SELECT 1 FROM payments p WHERE u.id = p.receiver)
;
output:
But this approach sometimes shows very poor performance in real circumstances.
So, below query would be another option you can use in that case.
SELECT id FROM user u WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.sender)
UNION ALL
SELECT id FROM user u WHERE EXISTS (SELECT 1 FROM payments p WHERE u.id = p.receiver)
;
I think below is the most optimal solution
select distinct id
from payments, unnest([sender, receiver]) id
join user
using(id)
if applied to sample data in your question - output is

SQL join and count rows (count people in table)

I have a table with users and a table with their posts. I do not understand how to calculate the number of users, for example, whose number of posts is less than 10.
select Users.DisplayName, count(Users.id) as Questions from Users
LEFT JOIN
Posts on Users.id=Posts.OwnerUserId
GROUP BY Users.DisplayName
HAVING (count(Users.Id)<10) or (count(Users.Id)>10 and count(Users.Id)<20)
If you want the number of users that have less than 10 records, than you can simply use a COUNT:
SELECT
COUNT(a.DisplayName)
FROM
Users a
LEFT JOIN Posts b ON a.id = b.OwnerUserId
HAVING
COUNT(b.Id) < 10
You can see that here using the data explorer.

How create SQL pagination difficult join query with duplicate data?

I have several tables in the database.
Users, profiles and user roles.
The relationship of profiles and users one to one.
The relationship of roles and users many to many.
To select all users, I send the following request:
SELECT A.role_id, A.role_name, A.user_id,B.user_username, B.user_password, B.profile_color_text, B.profile_color_menu, B.profile_color_bg FROM
(SELECT Roles.role_id, Roles.role_name, UserRoles.user_id
FROM Roles INNER JOIN UserRoles ON Roles.role_id = UserRoles.role_id) AS A
LEFT JOIN
(SELECT Users.user_username, Users.user_password, Profiles.profile_color_text, Profiles.profile_color_menu, Profiles.profile_color_bg, Profiles.profile_id
FROM Users INNER JOIN Profiles ON Users.user_id = Profiles.profile_id) AS B
ON A.user_id = B.profile_id;
The question is how do I select a pagination?
I would get the 10 users first, then perform the joins. Two reasons for this:
Since you don't want specifically 10 results but just the results of 10 users, which could contain any number of rows, you can't get all the data then limit it, otherwise you could be getting 10 rows containing data for 5 users;
Even if point 1 were irrelevant because there was always a 1-1 relationship, and especially if the number of results is small like 10, it's faster to get those results first and then join on that smaller "table", rather than doing all your joins on all the data and then limiting it.
.
SELECT
u.user_id,
u.user_username,
u.user_password,
r.role_id,
r.role_name,
p.profile_id,
p.profile_color_text,
p.profile_color_menu,
p.profile_color_bg
FROM (
SELECT user_id, user_username, user_password
FROM users
ORDER BY ???
OFFSET 10
LIMIT 10
) AS u
LEFT JOIN profiles AS p
ON u.user_id = p.profile_id
LEFT JOIN userroles AS ur
ON u.user_id = ur.user_id
LEFT JOIN roles AS r
ON ur.role_id = r.role_id
I assume you'll want some order, so I've put an ORDER BY in there - to be completed.
OFFSET added to get the second page of results; first page wouldn't require it, or would be OFFSET 0. Then a LIMIT of course to limit the page size.
I've also restructured the joins in a way that made more sense to me.

How can I join on only a certain number of rows

I want to join two tables on a particular column, but I only want to join on 50 of the rows from the first table. I.e. I want to do the following:
select * from s1.companies c limit 50 join s2.employees e on c.id = e.c_id;
I'm getting a syntax error because of the limit. How can I do this query? The reason I want to do this is because the companies table has millions of rows and I just want to play with some of the data without having long running queries.
You need a subquery.
select * from (select * from s1.companies limit 50) c join s2.employees e on c.id = e.c_id;