SQL query that I can only get the first half of - sql

Hello I have a question regarding a query I am trying to create. I want to create a query that will List members'name for who ever checked out one oe more books and have returned ALL of them. I have a query (shown below that finds the entries if there Return Date is no NULL but I can't figure out how to now show the Names if they have a returned book but still have another book they have no returned (Return Date = NULL). Below is my script if anybody can give me some advice. Thank you.
SELECT MemName
FROM MEMBER, CHECK_OUT
WHERE MEMBER.ID = CHECK_OUT.MemID
AND CHECK_OUT.DateRet IS NOT NULL

If I understand what you are asking, you want a query that will return all members who currently have a book checked out, correct?
Something like this might be what you want:
SELECT
M.MemName
FROM
MEMBER AS M
INNER JOIN CHECK_OUT AS C ON (C.MemID = M.ID)
WHERE
C.DateRet IS NULL
This will list all member names that have something checked out, but will not remove duplicates. To list each name only one, use SELECT DISTINT. To get the names who do NOT have anything checked out anymore (but did at one point) use either your query or modify mine to say C.DateRet IS NOT NULL. I personally just like using INNER JOINS in this situation.

I want to create a query that will List members' names for who ever checked out one or more books and have returned ALL of them.
SELECT MEMBER.MemName
FROM MEMBER, CHECK_OUT
WHERE MEMBER.ID = CHECK_OUT.MemID
group by MEMBER.ID, MEMBER.MemName
having max(nvl2(CHECK_OUT.DateRet, 0, 1)) = 0

Related

New to SQL and need some advice with queries

I have recently started learning SQL but can't seem to get my head around creating SQL statements that form relevant results from multiple tables/relations.
Given the following schema:
Account(accNumber, balance, type)
Branch(BSB, phone, streetAddress, town)
registered(accNumber*, BSB*)
I am trying to formulate some outputs:
List all the accNumber registered with a specific BSB (123) and show its listed town (Sydney).
I have tried the following statement for the first query:
SELECT accNumber, BSB, town
FROM ACCOUNT, BRANCH
WHERE BSB = 123;
However, I get every account listed even if they don't belong to the BSB, so I tried:
SELECT accNumber, BSB, town
FROM ACCOUNT, BRANCH
WHERE BSB = 123
AND Town = 'Sydney'
AND account.accNumber=registered.accNumber
AND branch.bsb=registered.bsb;
This time I get column ambiguously defined because they have the same name in the "registered" table.
I've tried making alias in the select statment i.e. accNumber AS ACCOUNT_NUMBER etc, but still getting ambiguously defined errors.
I tried just listing what was in the registered table but then I do not get the town name, just the accNumber and the BSB passed in as a foreign key.
I can't seem to understand how to pull data from other tables and display them correctly and would greatly appreciate any advice!
This might help you start.
SELECT a.ccNumber, b.BSB, c.town
FROM ACCOUNT as a
inner join registered as b on b.accNumber=a.accNumber
inner join BRANCH as c on c.bsb = b.bsb
WHERE b.BSB = 123
AND c.Town = 'Sydney'
So this sounds like a generic SQL question. For your query here is what you're looking at:
select a.account_number
from account a, brance b, registered r
where a.account_number = r.account_number and
a.bsb = b.bsb and
b.bsb = 123;
This will get you all account numbers from the account table that are in BSB 123. When you have multiple tables that have the same column, you need to tell Oracle (and any database for that matter) which "account_number" column you're referring to (as otherwise it's ambiguous as there are multiple tables that contain the column account_number).
SQL is about tables and joins. You sometimes have to join several tables to get what you need, as above. If you don't join tables, as you did originally, you'll get a "cross product", which is not what you want.
I know this is very "light", but hopefully from the above answer to your question, you'll get some idea of how to do this.
I'd be happy to help you more if you have questions. Everyone is new to SQL at some point. Don't feel bad about that. It takes practice and then it becomes much easier.
-Jim

TSQL: Returning a value of 'NONE' if rows don't exist

I have been working with Microsoft Server Management Studio for the past few weeks and for the most part I've been able to figure everything out, but there's one major area I'm having difficulty. I'm routinely asked to run queries to find specific values for people in a given list of names. However sometimes some of the people in the lists I query over don't have the values I'm looking for, so their names aren't returned at all.
For example when I put in a list of 50 people to this query:
SELECT p.name, dbsr.reviewCd
FROM Person p, DbSpecialReview dbsr
WHERE p.personId = dbsr.personId
AND p.name IN (...);
I might only get values for 40.
What I need is to know how to run a query such that even if only 40/50 people have values associated with them, the query returns a table containing all of their names, and if a person doesn't have a value it populates the relevant column with 'None' or 'Null'.
SELECT p.name, ISNULL(dbsr.reviewCd , 'None') AS reviewCd
FROM Person p
LEFT JOIN DbSpecialReview dbsr ON p.personId = dbsr.personId
AND p.name IN (...);

Is there a way to select automatically the row pointed by an FK on a given table?

Today while writing one of the many queries that every developer in my company write every day I stumbled upon a question.
The DBMS we are using is Sql Server 2008
Say for example I write a query like this in the usual PERSON - DEPARTMENT db example
select * from person where id = '01'
And this query returns one row:
id name fk_department
01 Joe dp_01
The question is: is there a way (maybe using an addon) to make sql server write and execute a select like this
select * from department where id = 'dp_01'
only by for example clicking with the mouse on the cell containing the fk value (dp_01 in the example query)? Or by right click and selecting something like ("Go to pointed value")?
I hope I didn't wrote something stupid or impossible by definition
Not really, but that seems like a silly thing to do. Why would you want to confuse an id with a department name?
Instead, you could arrange things so you could do:
select p.*
from person p
where department = 'dp_01';
You would do this by adding a computed column department that references a scalar function that looks up the value in the department table. You can read about computed columns here.
However, a computed column would have bad performance characteristics. In particular, it would basically require a full table scan on the person table, even if that is not appropriate.
Another solution is to create a view, v_person that has the additional columns you want. Then you would do:
select p.*
from v_person p
where department = 'dp_01';
Why can't you write yourself by saying
select * from department where id =
(select fk_department from person where id = '01')

SQL latest entry

So this is most likely a long shot.
I have a table named 'Case' that has primary key 'Case_ID'. In another table named 'CaseStatus' there is a column named 'value' which just has items like pending, researching,... etc.
My issue is everytime the status is updated, I am getting duplicate 'Case_ID's
How can I select the latest entry (essentially showing the current 'status')?
There is no date, however both tables share a column named 'Last_Mod_Date'. I am not sure if that helps. I have done tons of searching on here and most people are looking for something like a MAX DATE but my brain doesn't want to work for some reason and I cannot connect the dots.
I appreciate everyone's help. I have fixed it. I'd post it but probably won't make sense without seeing the whole picture... here is the relevant part I think:
where
B.[CASE_ID] = A.CASE_ID
and B.LAST_ONE = 'Y'
and C.[TYPE] = 'STATUS'
and [DIS].[TYPE] = 'DISPOSITION'
In the CaseStatus table you need to add a status change date. Without that you have no reliable way of getting the latest status.
Another option is to use an UPDATE instead of an INSERT when changing status. That way you will always have one row per case ID.
This will give you one row for the case and one for the case status.
select *
from [CASE] C
inner join
(select cs.CASE_ID, cs.Status
from CaseStatus cs
where C.[TYPE] = 'STATUS'
and cs.CaseStatus_ID in
(select max(s.CaseStatus_ID) maxID
from CaseStatus s
where s.[TYPE] = 'STATUS'
group by s.CASE_ID)) as cs
on c.Case_ID = cs.Case_ID
Normally you would just have a CASE.CaseStatusID column that would hold the latest status and then you would use triggers to capture the history if you ever needed it. Also, I would stay away from using reserved words like CASE for my object names.

SQL: selective subqueries

I'm having an SQL query (MSSQLSERVER) where I add columns to the resultset using subselects:
SELECT P.name,
(select count(*) from cars C where C.type = 'sports') AS sportscars,
(select count(*) from cars C where C.type = 'family') AS familycars,
(select count(*) from cars C where C.type = 'business') AS businesscars
FROM people P
WHERE P.id = 1;
The query above is just from a test setup that's a bit nonsense, but it serves well enough as example I think. The query I'm actually working on spans a number of complex tables which only distracts from the issue at hand.
In the example above, each record in the table "people" also has three additional columns: "wantsSportscar", "wantsFamilycar" and "wantsBusinesscar". Now what I want to do is only do the subselect of each additional column if the respective "wants....." field in the people table is set to "true". In other words, I only want to do the first subselect if P.wantsSportscar is set to true for that specific person. The second and third subselects should work in a similar manner.
So the way this query should work is that it shows the name of a specific person and the number of models available for the types of cars he wants to own. It might be worth noting that my final resultset will always only contain a single record, namely that of one specific user.
It's important that if a person is not interested in a certain type of cars, that the column for that type will not be included in the final resultset. An example to be sure this is clear:
If person A wants a sportscar and a familycar, the result would include the columns "name", "sportscars" and "familycars".
If person B wants a businesscar, the result would include the columns "name" and "businesscar".
I've been trying to use various combinations with IF, CASE and EXISTS statements, but so far I've not been able to get a syntactically correct solution. Does anyone know if this is even possible? Note that the query will be stored in a Stored Procedure.
In your case, there are 8 column layouts possible and to do this, you will need 8 separate queries (or build your query dynamically).
It's not possible to change the resultset layout within a single query.
Instead, you may design your query as follows:
SELECT P.name,
CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars,
CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars,
CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars
FROM people P
WHERE P.id = 1
which will select NULL in appropriate column if a person doesn't want it, and parse these NULL's on client side.
Note that relational model answers the queries in terms of relations.
In your case, the relation is as follows: "this person needs are satisifed with this many sport cars, this many business cars and this many family cars".
Relational model always answers this specific question with a quaternary relation.
It doesn't omit any of the relation members: instead, it just sets them to NULL which is the SQL's way to show that the member of a relation is not defined, applicable or meaningful.
I'm mostly an Oracle guy but there's a high chance the same applies. Unless I've misunderstood, what you want is not possible at that level - you will always have a static number of columns. Your query can control if the column is empty but since in the outer-most part of the query you have specified X number of columns, you are guaranteed to get X columns in your resultset.
As I said, I am unfamiliar with MS SQL Server but I'm guessing there will be some way of executing dynamic SQL, in which case you should research that since it should allow you to build a more flexible query.
You may be able to do what you want by first selecting the values as separate rows into a temp table, then doing a PIVOT on that table (turning the rows into columns).
It's important that if a person is not
interested in a certain type of cars,
that the column for that type will not
be included in the final resultset. An
example to be sure this is clear:
You will not be able to do it in plain SQL. I suggest you just make this column NULL or ZERO.
If you want the query to be dynamically expand when new cars are added, then PIVOTing could help you somewhat.
There are three fundamentals you want to learn to make this work easy. The first is data normalization, the second is GROUP BY, and the third is PIVOT.
First, data normalization. Your design of the people table is not in first normal form. The columns "wantsports", "wantfamily", "wantbusiness" are really a repeating group, although they may not look like one. If you can modify the table design, you will find it advantageous to create a third table, lets call it "peoplewant", with two key columns, personid and cartype. I can go into detail about why this design will be more flexible and powerful if you like, but I'm going to skip that for now.
On to GROUP BY. This allows you to produce a result that summarizes each group in one row of the result.
SELECT
p.name,
c.type,
c.count(*) as carcount
FROM people p,
INNER JOIN peoplewant pw ON p.id = pw.personid
INNER JOIN cars c on pw.cartype = c.type
WHERE
p.id = 1
GROUP BY
p.name,
c.type
This (untested) query gives you the result you want, except that the result has a separate row for each car type the person wants.
Finally, PIVOT. The PIVOT tool in your DBMS allows you to turn this result into a form where there is just one row for the person, and there is a separate column for each of the cartypes wanted by that person. I haven't used PIVOT myself, so I'll let somebody else edit this response to provide an example using PIVOT.
If you use the same technique to retrieve data for multiple people in one sweep, keep in mind that a column will appear for each wanted type that any person wants, and zeroes will appear in the PIVOT result for persons who do not want a car type that is in the result columns.
Just came across this post through a google search, so I realize I'm late to this party by a bit, but .. sure this really is possible to do... however, I wouldn't suggest actually doing it this way because it's usually considered a Very Bad Thing (tm).
Dynamic SQL is your answer.
Before I say how to do it, I want to preface this with, Dynamic SQL is a very dangerous thing, if you aren't sanitizing your input from the application.
So, therefore, proceed with caution:
declare #sqlToExecute nvarchar(max);
declare #includeSportsCars bit;
declare #includeFamilyCars bit;
declare #includeBusinessCars bit;
set #includeBusinessCars = 1
set #includeFamilyCars = 1
set #includeSportsCars = 1
set #sqlToExecute = 'SELECT P.name '
if #includeSportsCars = 1
set #sqlToExecute = #sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, ';
if #includeFamilyCars = 1
set #sqlToExecute = #sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, ';
if #includeBusinessCars = 1
set #sqlToExecute = #sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars '
set #sqlToExecute = #sqlToExecute + ' FROM people P WHERE P.id = 1;';
exec(#sqlToExecute)