SQL Server Select data excluding Junction table - sql

i need to select data that is not present in the junction table
so got three tables
trialsTable (trialID,TrialName)
VolunteerTAble(volunteerID, VolunteerName)
JunctionTAble(JunctionTableIs,TrialID,VolunteerName)
for every trial an email is sent to volunteers, but next time i want to exclude the volunteers which have received email for that trial and only send email to volunteers which have not received email for that trial, hence i created the junction table which have many to many relationship for the trial and volunteers

Assuming you have a variable or something for the #TrialId which you currently want something like:
SELECT *
FROM VolunteerTable v
WHERE NOT EXISTS(SELECT 1
FROM JunctionTable j
WHERE j.VolunteerName = v.VolunteerName
AND j.TrialID = #TrialId)
And you should probably put volunteerID in you juntion table instead of name.

Related

Getting values from 3 tables where two of those are self-joined

Problem Introduction:
I'm working on a faker e-banking system as a side project for practicing my SQL skills (which are super bad), and I'm now stuck at a point where I want to pull data from two tables but cannot do that with joins
So, basically I have a users table which looks like this:
And I have a transactions table which saves the sender id, receiver id, amount, and date. And it looks like this:
What I want to achieve:
Now, I want to create a query that extracts the data in the transactions table in a way such that if I specified an ID of n (somewhere in the query), I'd get all of the transactions that user with ID of n made in a way that it displays the first name of both sender and receiver of all of theses transactions.
I've been doing a lot of joins/subquery stuff to extract that information but I really can't seem to find a solution.
I'm using PostgreSQL btw.
Notes:
Sender & Receiver are not the same person
I want to display the first name of both the sender & receiver
You can achieve this by joining to the users table twice (you'll have to give it a separate name for each join). The following snippet leaves out some detail but illustrates the idea -
SELECT
...
FROM
transactions t
INNER JOIN users s ON t.sender = s.id
INNER JOIN users r ON t.receiver = r.id
in the transaction table I understand that the sender and receiver columns are the ids of the customers in the users table.
Filter all the transactions in which a jane user with id 17 has received a transaction (for simplicity I will use *, but ideally you should select the columns you need)
select * from users, transactions where users.id=transactions.receiver and users.id='17'.
the same but for jane shipments
select * from users, transactions where users.id=transactions.sender and users.id='17'.
if you want all the jane transactions
select * from users, transactions where users.id=transactions.receiver and users.id=transactions.sender and users.id='17'.
if you want to further filter by an amount of money sent
select * from users, transactions where users.id=transactions.sender and users.id='17' and amount > 100
finally
select first_name from users, transactions where users.id=transactions.receiver and users.id=transactions.sender and (users.id='17' or users.id='18' )
finally finally
select first_name from users, transactions where users.id=transactions.receiver and users.id=transactions.sender

How to compare attributes from tables that are 3 relationships apart in SQL

I need to produce some sample SQL for a database to be created with an ERD that I have already made.
The required query is https://imgur.com/vDvL1ZS
and the ERD being used is https://imgur.com/a/hzTQhPU
I've assumed that I need to select from the 3 tables (Message, Chat, UserChat) but I don't understand how the query would be able to match up each instance of messages and users. I've tried to make somewhat of a starting point below but can't see what else would be required.
SELECT MessageID, ChatID, IsText, MessageText, FileName, MessageFile
FROM tbl_Message, tbl_Chat, tbl_UserChat
WHERE tbl_Message.DateSent > UserChat.LastOpened
AND tbl_User.UserID = ‘1044’
AND tbl_Chat.ChatID = ‘139’
A primary key is a field or set of fields which are unique across your table. Each row has its unique primary key.
A foreign key is a field or set of fields, containing only values that are another's table primary key. It is basically a reference to another table.
You can link tables that have references to each others using joins. Here, you are trying to link the Message, Chat and UserChat tables, like so:
select
*
from
UserChat
join
Chat on UserChat.ChatID = Chat.ChatID
join
Message on Chat.ChatID = Message.ChatID
As you can see, I "chained" the joins as the relationships are in the diagram.
Now we got our tables linked, we can add our filters, and only select the Message fields:
select
Message.*
from
UserChat
join
Chat on UserChat.ChatID = Chat.ChatID
join
Message on Chat.ChatID = Message.ChatID
where
UserChat.ChatID = y -- Filter the correct chat
and
UserChat.MessageID = x -- Filter the correct user
and
UserChat.LastOpened < Message.DateSent -- Filter only unread messages

How to tightly contain an SQL query result

I'm writing an application that implements a message system through a 'memos' table in a database. The table has several fields that look like this:
id, date_sent, subject, senderid, recipients,message, status
When someone sends a new memo, it will be entered into the memos table. A memo can be sent to multiple people at the same time and the recipients userid's will be inserted into the 'recipients' field as comma separated values.
It would seem that an SQL query like this would work to see if a specific userid is included in a memo:
SELECT * FROM memos WHERE recipients LIKE %15%
But I'm not sure this is the right solution. If I use the SQL statement above, won't that return everything that "contains" 15? For example, using the above statement, user 15, 1550, 1564, 2015, would all be included in the result set (and those users might not actually be on the recipient list).
What is the best way to resolve this so that ONLY the user 15 is pulled in if they are in the recipient field instead of everything containing a 15? Am I misunderstanding the LIKE statement?
I think you would be better off having your recipients as a child table of the memos table. So your memo's table has a memo ID which is referenced by the child table as
MemoRecipients
-----
MemoRecipientId INT PRIMARY KEY, IDENTITY,
MemoId INT FK to memos NOT NULL
UserId INT NOT NULL
for querying specific memos from a user you would do something like
SELECT *
FROM MEMOS m
INNER JOIN memoRecipients mr on m.Id = mr.memoId
WHERE userId = 15
No, you aren't misunderstood, that's how LIKE works.. But to achieve what you want, it would be better not to combine the recipients into 1 field. Instead try to create separate table that saves the recipient list for each memo..
For me I will use below schema, for your need:
Table_Memo
id, date_sent, subject, senderid, message, status
Table_Recipient
id_memo FK Table_Memo(id), recipient
By doing so, if you want to get specific recipients from a memo, you can do such query:
SELECT a.* FROM Table_Memo a, Table_Recipient b
WHERE a.id = "memo_id" AND a.id = b.id_memo AND b.recipient LIKE %15%
I am not sure how your application is exactly pulling these messages, but I imagine that better way would be creating a table message_recepient, which will represent many-to-many relationship between recipients and memos
id, memoId, recepientId
Then your application could pull messages like this
SELECT m.*
FROM memos m inner join message_recepient mr on m.id = mr.memoId
WHERE recepientId = 15
This way you will get messages for the specific user. Again, don't know what your status field is for but if this is for new/read/unread, you could add in your where
and m.status = 'new'
Order by date_set desc
This way you could just accumulate messages, those that are new

SQL Cross-Table Referencing

Okay, so I've got two tables. One table (table 1) contains a column Books_Owned_ID which stores a series of numbers in the form of 1,3,7. I have another table (table 2) which stores the Book names in one column and the book ID in another column.
What I want to do is create an SQL code which will take the numbers from Books_Owned_IDand display the names of those books in a new column. Like so:
|New Column |
Book 1 Name
Book 2 Name
Book 3 Name
I can't wrap my head around this, it's simple enough but all the threads I look on get really confusing.
Table1 contains the following columns:
|First_Name| Last_Name| Books_Owned_ID |
Table2 contains the following columns:
|Book_Name|Book_ID|
You need to do an inner join. This is a great example/reference for these
SELECT Book_Name FROM Table2
INNER JOIN Table1
ON Table1.Books_Owned_ID = Table2.Book_ID
EDIT SQL Fiddle
I will work on getting the column comma split working. It wont be a lot extra for this.
EDIT 2 See this answer to build a function to split your string. Then you can do this:
SELECT Book_Name FROM Table2
WHERE Book_ID IN(SELECT FN_ListToTable(',',Table1.Books_Owned_ID) FROM Table1 s)
The core of this centers around data normalisation... Each fact is stored only once (and so is "authoritative"). You should also get into the habit of only storing a single fact in any field.
So, imagine the following table layouts...
Books
Id, Name, Description
Users
Id, Username, EmailAddress, PasswordHash, etc....
BooksOwned
UserId, BookId
So if a single user owns multiple books, there will be multiple entries in the BooksOwned table...
UserId, BookID
1, 1
1, 2
1, 3
Indicates that User 1 owns books 1 through 3.
The reason to do it this way is that it makes it much easier to query in future. You also treat BookId as an Integer instead of a string containing a list - so you don't need to worry about string manipulation to do your query.
The following would return the name of all books owned by the user with Id = 1
SELECT Books.Name
FROM BooksOwned
INNER JOIN Books
ON BooksOwned.BookId = Books.Id
WHERE BooksOwned.UserId = 1
You need a function which takes a comma separated list and returns a table. This is slow and fundamentally a bad idea. Really all this does is convert this way of doing it to be like the data model I describe below. (see ProfessionalAmateur's answer for an example of this).
If you are just starting change your data model. Make a linking table. Like this:
Okay, so I've got two tables. One table (table 1) contains a column Books_Owned_ID which stores a series of numbers in the form of 1,3,7. I have another table (table 2) which stores the Book names in one column and the book ID in another column.
What I want to do is create an SQL code which will take the numbers from Books_Owned_IDand display the names of those books in a new column. Like so:
Person Table
|First_Name| Last_Name| Person_ID |
Book Table
|Book_Name|Book_ID|
PersonBook Table
|PersonID|BookID|
This table can have more than one row for each person.

Stored Procedure to get the count using 2 tables

I am trying to write a complicated stored procedure for the first time. My goal is to get the count with some condition from 2 tables.
Consider Merchant table and Email table.
Email table saves the Email invitations sent by the Merchant. Merchant table has all the Merchant Info along with Email IDs.
My goal is to get the count of EmailID s that are in the Merchant table by checking if the Email invitations sent by Merchant has signed up.
I have tried to make this question clear... Hope i am clear.
Thanks in advance..
Why a stored procedure? It sounds like it can be done in a single SQL query.
Let's see if I understand your question correctly: Merchants invite other people to become a Merchant as well and you want a list with the number of accepted invitations per merchant?
Something along those lines:
select MerchantName, count(1)
from Merchants, Emails
where Merchants.Id = Emails.Id
and Emails.SignedUp = 'YES!'
group by MerchantName;
It sounds like there is some confusion in that schema you're describing. Based on what I think you're trying to do I'd suggest you have a Merchant table, an Email table and a MerchantEmail table which links Merchants to email invitations sent.
The way it appears otherwise with a Merchant table that has an Email ID is a many-to-one relationship so that several Merchants could be the recipients of an email. In that case the Signed Up flag would appear in the Merchants table and not in the Email table.
CREATE PROCEDURE GetMerchantsSignedUp
#EmailId INT
AS
SELECT COUNT(*) AS MerchantSignedUp
FROM Merchant, Email
WHERE Merchant.EmailId = Email.Id
AND Merchant.SignedUp = 1
AND Email.Id = #EmailId
Please note that there is some redundancy above which suggests that you need not even include the Email table in the query. Here it acts only to indicate a foreign key relationship.