Sql syntax to always get one - vb.net

SELECT dbo.Calls.Description, dbo.TicketRead.IsRead, dbo.TicketRead.UserID
FROM dbo.Calls
LEFT OUTER JOIN dbo.TicketRead ON dbo.Calls.CallID = dbo.TicketRead.TicketID
WHERE dbo.TicketRead.UserID = 1 or is null
I want to get a list of all calls, but also a value indicating if the user have read the call. so I made when a user open's the ticket a new record is added in a special table.
now if no user have read it then it's ok, but if just one user have read this ticket, then the other users don't have this call in the list..
I'm using msSql and .net entity framework.

Are you sure you want to limit the TicketRead table to just UserID # 1?
SELECT c.Description, r.IsRead, r.UserID
FROM dbo.Calls c
LEFT JOIN dbo.TicketRead r
ON c.CallID = r.TicketID
AND r.UserID = 1
This is done joining on multiple conditions... You'll get all of the Calls, but only the IsRead flag from TicketRead when UserID #1 has read it.
I'm not convinced this is actually what you're looking for, and I will be available to update this later if not.
Also please consider using table aliases in the future, I think you'll like it.

you are wanting to do the following:
SELECT dbo.Calls.Description, dbo.TicketRead.IsRead, dbo.TicketRead.UserID
FROM dbo.Calls
LEFT OUTER JOIN dbo.TicketRead ON dbo.Calls.CallID = dbo.TicketRead.TicketID
WHERE
dbo.TicketRead.UserID = 1
or dbo.TicketRead.CallID IS NULL
basically, we are saying here, if there a userid, it has to be 1. otherwise, the records primary key is null (because of the join). Fosco's answer is also right, and much smaller :)

Related

Complex SQL View with Joins & Where clause

My SQL skill level is pretty basic. I have certainly written some general queries and done some very generic views. But once we get into joins, I am choking to get the results that I want, in the view I am creating.
I feel like I am almost there. Just can't get the final piece
SELECT dbo.ics_supplies.supplies_id,
dbo.ics_supplies.old_itemid,
dbo.ics_supplies.itemdescription,
dbo.ics_supplies.onhand,
dbo.ics_supplies.reorderlevel,
dbo.ics_supplies.reorderamt,
dbo.ics_supplies.unitmeasure,
dbo.ics_supplies.supplylocation,
dbo.ics_supplies.invtype,
dbo.ics_supplies.discontinued,
dbo.ics_supplies.supply,
dbo.ics_transactions.requsitionnumber,
dbo.ics_transactions.openclosed,
dbo.ics_transactions.transtype,
dbo.ics_transactions.originaldate
FROM dbo.ics_supplies
LEFT OUTER JOIN dbo.ics_orders
ON dbo.ics_supplies.supplies_id = dbo.ics_orders.suppliesid
LEFT OUTER JOIN dbo.ics_transactions
ON dbo.ics_orders.requisitionnumber =
dbo.ics_transactions.requsitionnumber
WHERE ( dbo.ics_transactions.transtype = 'PO' )
When I don't include the WHERE clause, I get 17,000+ records in my view. That is not correct. It's doing this because we are matching on a 1 to many table. Supplies table is 12,000 records. There should always be 12,000 records. Never more. Never less.
The pieces that I am missing are:
I only need ONE matching record from the ICS_Transactions Table. Ideally, the one that I want is the most current 'ICS_Transactions.OriginalDate'.
I only want the ICS_Transactions Table fields to populate IF ICS_Transacions.Type = 'PO'. Otherwise, these fields should remain null.
Sample code or anything would help a lot. I have done a lot of research on joins and it's still very confusing to get what I need for results.
EDIT/Update
I feel as if I asked my question in the wrong way, or didn't give a good overall view of what I am asking. For that, I apologize. I am still very new to SQL, but trying hard.
ICS_Supplies Table has 12,810 records
ICS_Orders Table has 3,666 records
ICS_Transaction Table has 4,701 records
In short, I expect to see a result of 12,810 records. No more and no less. I am trying to create a View of ALL records from the ICS_Supplies table.
Not all records in Supply Table are in Orders and or Transaction Table. But still, I want to see all 12,810 records, regardless.
My users have requested that IF any of these supplies have an open PO (ICS_Transactions.OpenClosed = 'Open' and ICS_Transactions.InvType = 'PO') Then, I also want to see additional fields from ICS_Transactions (ICS_Transactions.OpenClosed, ICS_Transactions.InvType, ICS_Transactions.OriginalDate, ICS_Transactions.RequsitionNumber).
If there are no open PO's for supply record, then these additional fields should be blank/null (regardless to what data is in these added fields, they should display null if they don't meet the criteria).
The ICS_Orders Table is nly needed to hop from the ICS_Supplies to the ICS_Transactions (I first, need to obtain the Requisition Number from the Orders field, if there is one).
I am sorry if I am not doing a good job to explain this. Please ask if you need clarification.
Here's a simplified version of Ross Bush's answer (It removes a join from the CTE to keep things more focussed, speed things up, and cut down the code).
;WITH
ordered_ics_transactions AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY requisitionnumber
ORDER BY originaldate DESC
)
AS seq_id
FROM
dbo.ics_transactions
)
SELECT
s.supplies_id, s.old_itemid,
s.itemdescription, s.onhand,
s.reorderlevel, s.reorderamt,
s.unitmeasure, s.supplylocation,
s.invtype, s.discontinued,
s.supply,
t.requsitionnumber, t.openclosed,
t.transtype, t.originaldate
FROM
dbo.ics_supplies AS s
LEFT OUTER JOIN
dbo.ics_orders AS o
ON o.supplies_id = s.suppliesid
LEFT OUTER JOIN
ordered_ics_transactions AS t
ON t.requisitionnumber = o.requisitionnumber
AND t.transtype = 'PO'
AND t.seq_id = 1
This will only join the most recent transaction record for each requisitionnumber, and only if it has transtype = 'PO'
IF you want to reverse that (joining only transaction records that have transtype = 'PO', and of those only the most recent one), then move the transtype = 'PO' filter to be a WHERE clause inside the ordered_ics_transactions CTE.
You can possibly work with the query below to get what you need.
1. I only need ONE matching record from the ICS_Transactions Table. Ideally, the one that I want is the most current 'ICS_Transactions.OriginalDate'.
I would solve this by creating a CTE with all the ICS_Transaction fields needed in the query, rank-ordered by OPriginalDate, partitioned by suppliesid.
2. I only want the ICS_Transactions Table fields to populate IF ICS_Transacions.Type = 'PO'. Otherwise, these fields should remain null.
If you move the condition from the WHERE clause to the LEFT JOIN then ICS_Transactions not matching the criteria will be peeled and replaced with null values with the rest of the query records.
;
WITH ReqNumberRanked AS
(
SELECT
dbo.ICS_Orders.SuppliesID,
dbo.ICS_Transactions.RequisitionNumber,
dbo.ICS_Transactions.TransType,
dbo.ICS_Transactions.OriginalDate,
dbo.ICS_Transactions.OpenClosed,
RequisitionNumberRankReversed = RANK() OVER(PARTITION BY dbo.ICS_Orders.SuppliesID, dbo.ICS_Transactions.RequisitionNumber ORDER BY dbo.ICS_Transactions.OriginalDate DESC)
FROM
dbo.ICS_Orders
LEFT OUTER JOIN dbo.ICS_Transactions ON dbo.ICS_Orders.RequisitionNumber = dbo.ICS_Transactions.RequsitionNumber
)
SELECT
dbo.ICS_Supplies.Supplies_ID, dbo.ICS_Supplies.Old_ItemID,
dbo.ICS_Supplies.ItemDescription, dbo.ICS_Supplies.OnHand,
dbo.ICS_Supplies.ReorderLevel, dbo.ICS_Supplies.ReorderAmt,
dbo.ICS_Supplies.UnitMeasure,
dbo.ICS_Supplies.SupplyLocation, dbo.ICS_Supplies.InvType,
dbo.ICS_Supplies.Discontinued, dbo.ICS_Supplies.Supply,
ReqNumberRanked.RequsitionNumber,
ReqNumberRanked.OpenClosed,
ReqNumberRanked.TransType,
ReqNumberRanked.OriginalDate
FROM
dbo.ICS_Supplies
LEFT OUTER JOIN dbo.ICS_Orders ON dbo.ICS_Supplies.Supplies_ID = dbo.ICS_Orders.SuppliesID
LEFT OUTER JOIN ReqNumberRanked ON ReqNumberRanked.RequisitionNumber = dbo.ICS_Transactions.RequsitionNumber
AND (ReqNumberRanked.TransType = 'PO')
AND ReqNumberRanked.RequisitionNumberRankReversed = 1

Beginner SQL trouble with simple Join/Subquery

I know this is a quite a basic query, but I can't find exactly the solution for my needs, so here goes...
I have a table of customers INT_AUX_LISTING, a table of folders in which they're contained INT_AUX_DIRECTORY and a joining table INT_AUX_DIR_LIST.
I need to return a list of all customers who are in folder 40017, and also not in folder 2. Any other folder in which they're contained is irrelevant.
So far I've come up with
SELECT *
FROM INT_AUX_LISTING l
LEFT JOIN INT_AUX_DIR_LIST dl
ON l.LISTING_ID=dl.LISTING_ID
WHERE dl.CONTAIN_DIR_ID=40017
AND dl.CONTAIN_DIR_ID <> 2
However this (obviously) isn't correct, and is returning far too many values. I think I need to introduce a subquery but things go awry when I try.
Apologies for the entry level nature of the question, but it's only my 3rd day working with SQL!
First, clarification on left-join and where. If you have a query that has a left-join to a table (per your example where "dl" was the right-side of the join), and then add the "dl" alias to a where clause, that in essence converts it to an INNER JOIN (unless you explicitly tested with an OR dl.listing_ID IS NULL).
Now, back to your primary objective. Without the table structures, and not exactly clear on your column intent, it APPEARS that the "listing_id" column is your client as found in the int_aux_listing table. the "contain_dir_id" is the directory (one of many that may be possible) as found in the int_aux_dir_list.
That said, you want a list of all people who are in 40017 AND NOT in 2. For this, you can do a left-join but using the int_aux_listing table twice but with different aliases. Once you get the list of clients, you can then join that to your client table. Since you are new, take the sample one step at a time.
Just get clients within 40017
select
dl.listing_id
from
int_aux_dir_list dl
where
dl.contain_dir_id = 40017
Now, exclude those if they are found in directory 2
select
dl.listing_id
from
int_aux_dir_list dl
left join int_aux_dir_list dl2
on dl.listing_id = dl2.listing_id
AND dl2.contain_dir_id = 2
where
dl.contain_dir_id = 40017
AND dl2.listing_id IS NULL
By doing a left-join to the directory listing table a second time (via alias "dl2"), but specifically for its directory id = 2 (via AND dl2.contain_dir_id = 2), you are getting all clients from dl that are within directory 40017 and looking for that same client ID in the second instance ("dl2").
Here's the kicker. Notice the where clause is stating "AND dl2.listing_id IS NULL".
This is basically saying WHEN Looking at the DL2 instance, I only want records that DO NOT have a corresponding listing ID matched.
Now, wrap this up to get the client information you need. In this case, getting all columns from the directory listing (in case other columns of importance), AND getting all columns from the "l"isting table.
select
dl.*,
l.*
from
int_aux_dir_list dl
left join int_aux_dir_list dl2
on dl.listing_id = dl2.listing_id
AND dl2.contain_dir_id = 2
join INT_AUX_LISTING l
on dl.listing_id = l.listing_id
where
dl.contain_dir_id = 40017
AND dl2.listing_id IS NULL
Hopefully this clears things up for you some.
Your WHERE clause is the problem; it brings back ALL rows except the one where that id is not 2, including the one where id is 40017 (since it's not equal to 2). I think you should revisit it.
If you use a LEFT JOIN[1] on INT_AUX_LISTING, you will get all customers even if they don't have a corresponding folder. In your case it's best to use INNER JOIN[2] instead.
In the where clause, you're first statement selects customers who are in folder 40017 (dl.CONTAIN_DIR_ID=40017), because of this your second statement (dl.CONTAIN_DIR_ID <> 2) will always be true (40017 will never be equal to 2). So basically your selecting all customers in folder 40017.
Your notion of a subquery was correct. This is one way of getting the desired result:
SELECT *
FROM INT_AUX_LISTING l
INNER JOIN INT_AUX_DIR_LIST dl1
ON l.LISTING_ID = dl1.LISTING_ID
WHERE dl1.CONTAIN_DIR_ID = 40017
AND l.LISTING_ID NOT IN (
SELECT dl2.LISTING_ID FROM INT_AUX_DIR_LIST dl2 WHERE dl2.CONTAIN_DIR_ID = 2
)

In an EXISTS can my JOIN ON use a value from the original select

I have an order system. Users with can be attached to different orders as a type of different user. They can download documents associated with an order. Documents are only given to certain types of users on the order. I'm having trouble writing the query to check a user's permission to view a document and select the info about the document.
I have the following tables and (applicable) fields:
Docs: DocNo, FileNo
DocAccess: DocNo, UserTypeWithAccess
FileUsers: FileNo, UserType, UserNo
I have the following query:
SELECT Docs.*
FROM Docs
WHERE DocNo = 1000
AND EXISTS (
SELECT * FROM DocAccess
LEFT JOIN FileUsers
ON FileUsers.UserType = DocAccess.UserTypeWithAccess
AND FileUsers.FileNo = Docs.FileNo /* Errors here */
WHERE DocAccess.UserNo = 2000 )
The trouble is that in the Exists Select, it does not recognize Docs (at Docs.FileNo) as a valid table. If I move the second on argument to the where clause it works, but I would rather limit the initial join rather than filter them out after the fact.
I can get around this a couple ways, but this seems like it would be best. Anything I'm missing here? Or is it simply not allowed?
I think this is a limitation of your database engine. In most databases, docs would be in scope for the entire subquery -- including both the where and in clauses.
However, you do not need to worry about where you put the particular clause. SQL is a descriptive language, not a procedural language. The purpose of SQL is to describe the output. The SQL engine, parser, and compiler should be choosing the most optimal execution path. Not always true. But, move the condition to the where clause and don't worry about it.
I am not clear why do you need to join with FileUsers at all in your subquery?
What is the purpose and idea of the query (in plain English)?
In any case, if you do need to join with FileUsers then I suggest to use the inner join and move second filter to the WHERE condition. I don't think you can use it in JOIN condition in subquery - at least I've never seen it used this way before. I believe you can only correlate through WHERE clause.
You have to use aliases to get this working:
SELECT
doc.*
FROM
Docs doc
WHERE
doc.DocNo = 1000
AND EXISTS (
SELECT
*
FROM
DocAccess acc
LEFT OUTER JOIN
FileUsers usr
ON
usr.UserType = acc.UserTypeWithAccess
AND usr.FileNo = doc.FileNo
WHERE
acc.UserNo = 2000
)
This also makes it more clear which table each field belongs to (think about using the same table twice or more in the same query with different aliases).
If you would only like to limit the output to one row you can use TOP 1:
SELECT TOP 1
doc.*
FROM
Docs doc
INNER JOIN
FileUsers usr
ON
usr.FileNo = doc.FileNo
INNER JOIN
DocAccess acc
ON
acc.UserTypeWithAccess = usr.UserType
WHERE
doc.DocNo = 1000
AND acc.UserNo = 2000
Of course the second query works a bit different than the first one (both JOINS are INNER). Depeding on your data model you might even leave the TOP 1 out of that query.

Translating Oracle SQL to Access Jet SQL, Left Join

There must be something I'm missing here. I have this nice, pretty Oracle SQL statement in Toad that gives me back a list of all active personnel with the IDs that I want:
SELECT PERSONNEL.PERSON_ID,
PERSONNEL.NAME_LAST_KEY,
PERSONNEL.NAME_FIRST_KEY,
PA_EID.ALIAS EID,
PA_IDTWO.ALIAS IDTWO,
PA_LIC.ALIAS LICENSENO
FROM PERSONNEL
LEFT JOIN PERSONNEL_ALIAS PA_EID
ON PERSONNEL.PERSON_ID = PA_EID.PERSON_ID
AND PA_EID.PERSONNEL_ALIAS_TYPE_CD = 1086
AND PA_EID.ALIAS_POOL_CD = 3796547
AND PERSONNEL.ACTIVE_IND = 1
LEFT JOIN PERSONNEL_ALIAS PA_IDTWO
ON PERSONNEL.PERSON_ID = PA_IDTWO.PERSON_ID
AND PA_IDTWO.PERSONNEL_ALIAS_TYPE_CD = 3839085
AND PA_IDTWO.ACTIVE_IND = 1
LEFT JOIN PERSONNEL_ALIAS PA_LIC
ON PERSONNEL.PERSON_ID = PA_LIC.PERSON_ID
AND PA_LIC.PERSONNEL_ALIAS_TYPE_CD = 1087
AND PA_LIC.ALIAS_POOL_CD = 683988
AND PA_LIC.ACTIVE_IND = 1
WHERE PERSONNEL.ACTIVE_IND = 1 AND PERSONNEL.PHYSICIAN_IND = 1;
This works very nicely. Where I run into problems is when I put it into Access. I know, I know, Access Sucks. Sometimes one needs to use it, especially if one has multiple database types that they just want to store a few queries in, and especially if one's boss only knows Access. Anyway, I was having trouble with the ANDs inside the FROM, so I moved those to the WHERE, but for some odd reason, Access isn't doing the LEFT JOINs, returning only those personnel with EID, IDTWO, and LICENSENO's. Not everybody has all three of these.
Best shot in Access so far is:
SELECT PERSONNEL.PERSON_ID,
PERSONNEL.NAME_LAST_KEY,
PERSONNEL.NAME_FIRST_KEY,
PA_EID.ALIAS AS EID,
PA_IDTWO.ALIAS AS ID2,
PA_LIC.ALIAS AS LICENSENO
FROM ((PERSONNEL
LEFT JOIN PERSONNEL_ALIAS AS PA_EID ON PERSONNEL.PERSON_ID=PA_EID.PERSON_ID)
LEFT JOIN PERSONNEL_ALIAS AS PA_IDTWO ON PERSONNEL.PERSON_ID=PA_IDTWO.PERSON_ID)
LEFT JOIN PERSONNEL_ALIAS AS PA_LIC ON PERSONNEL.PERSON_ID=PA_LIC.PERSON_ID
WHERE (((PERSONNEL.ACTIVE_IND)=1)
AND ((PERSONNEL.PHYSICIAN_IND)=1)
AND ((PA_EID.PRSNL_ALIAS_TYPE_CD)=1086)
AND ((PA_EID.ALIAS_POOL_CD)=3796547)
AND ((PA_IDTWO.PRSNL_ALIAS_TYPE_CD)=3839085)
AND ((PA_IDTWO.ACTIVE_IND)=1)
AND ((PA_LIC.PRSNL_ALIAS_TYPE_CD)=1087)
AND ((PA_LIC.ALIAS_POOL_CD)=683988)
AND ((PA_LIC.ACTIVE_IND)=1));
I think that part of the problem could be that I'm using the same alias (lookup) table for all three joins. Maybe there's a more efficient way of doing this? Still new to SQL land, so any tips as far as that goes would be great. I feel like these should be equivalent, but the Toad query gives me back many many tens of thousands of imperfect rows, and Access gives me fewer than 500. I need to find everybody so that nobody is left out. It's almost as if the LEFT JOINs aren't working at all in Access.
To understand what you are doing, let's look at simplified version of your query:
SELECT PERSONNEL.PERSON_ID,
PA_EID.ALIAS AS EID
FROM PERSONNEL
LEFT JOIN PERSONNEL_ALIAS AS PA_EID ON PERSONNEL.PERSON_ID=PA_EID.PERSON_ID
WHERE PERSONNEL.ACTIVE_IND=1
AND PERSONNEL.PHYSICIAN_IND=1
AND PA_EID.PRSNL_ALIAS_TYPE_CD=1086
AND PA_EID.ALIAS_POOL_CD=3796547
If the LEFT JOIN finds match, your row might look like this:
Person_ID EID
12345 JDB
If it doesn't find a match, (disregard the WHERE clause for a second), it could look like:
Person_ID EID
12345 NULL
When you add the WHERE clauses above, you are telling it to only find records in the PERSONNEL_ALIAS table that meet the condition, but if no records are found, then the values are considered NULL, so they will never satisfy the WHERE condition and no records will come back...
As Joe Stefanelli said in his comment, adding a WHERE clause to a LEFT JOIN'ed table make it act as an INNER JOIN instead...
Further to #Sparky's answer, to get the equivalent of what you're doing in Oracle, you need to filter rows from the tables on the "outer" side of the joins before you join them. One way to do this might be:
For each table on the "outer" side of a join that you need to filter rows from (that is, the three instances of PERSONNEL_ALIAS), create a query that filters the rows you want. For example, the first query (say, named PA_EID) might look something like this:SELECT PERSONNEL_ALIAS.* FROM PERSONNEL_ALIAS WHERE PERSONNEL_ALIAS.PERSONNEL_ALIAS_TYPE_CD = 1086 AND PERSONNEL_ALIAS.ALIAS_POOL_CD = 3796547
In your "best shot in Access so far" query in the original post: a) replace each instance of PERSONNEL_ALIAS with the corresponding query created in Step 1, and, b) remove the corresponding conditions (on PA_EID, PA_IDTWO, and PA_LIC) from the WHERE clause.

Inner join only returns 7 records

I've got a table with 500.000 records filled with Twitter updates. Then I've got a table with user info.
I basically need all the Twitter records of the people in my user table.
I can do it with this SELECT IN query:
SELECT *
FROM STATUS WHERE twitterUserID
IN (
SELECT twitteruserid
FROM accountLink
)
But that's obviously very slow.
I then tried to do it with a join, but it only shows 7 records. No idea why.
SELECT status . * , accountLink.userId, accountLink.twitterUserId
FROM status
JOIN accountLink
ON status.twitterUserId = accountLink.twitterUserId
Does anyone know what could cause this behaviour and how to solve it?
Try changing it to this:
SELECT status.* , accountLink.userId, accountLink.twitterUserId
FROM status
LEFT JOIN accountLink
ON status.twitterUserId = accountLink.twitterUserId
I suspect that there aren't matches for all the records between status and account link. Doing a left join will select every status regardless of whether or not accountLink has a match.
The JOIN syntax should work, unless the column data types are different.
Per the MySQL Documentation for IN():
The search for the item then is done using a binary search. This means IN is very quick if the IN value list consists entirely of constants. Otherwise, type conversion takes place according to the rules described in Section 11.2, “Type Conversion in Expression Evaluation”, but applied to all the arguments.
Ensuring that your column types match should ensure that the JOIN syntax works correctly.
SELECT s.*, a.twitterUserId, a.userId
FROM status AS s INNER JOIN accountLink AS a
WHERE s.twitterUserId=a.twitterUserId
You DO want to use inner join because you only want to return results IF the "status" table has a record AND a corresponding user record is found in the "accountLink" table. If a "status" table record does NOT have a corresponding user entry, you shouldn't display it (at least according to your post). LEFT OUTER JOIN would display status table records even if there was not a matching entry in the accountLink table.
Here's a great resource for learning about SQL joins:
SQL Joins (w3schools.com)