SQL - Nested Sub-Queries - sql

Using postgresql, I have to create a view called 'no_cat_customers' that returns the first name, last name of any customer who has not been shipped any edition of the book named 'The Cat in the Hat'. This should be done using universal quantification. The error I'm getting is - "ERROR: EXCEPT types integer and text cannot be matched" in reference to line 7 (EXCEPT (SELECT shipments.isbn).
CREATE VIEW no_cat_customers(customer_first_name, customer_last_name)
AS SELECT first_name, last_name
FROM customers
WHERE EXISTS (SELECT c_id
FROM shipments
WHERE customers.c_id = shipments.c_id
EXCEPT (SELECT shipments.isbn
FROM books, editions, shipments
WHERE books.book_id = editions.book_id AND
editions.isbn = shipments.isbn AND
title LIKE '%The Cat in the Hat%'));
I understand that this is hard to ask considering you don't have the database I'm using, but any help with this would be very much appreciated!
EDIT: I should add that there is two editions of 'The Cat in the Hat' in the database, both with different isbn's. So that has to be taken into account.

Use explicit JOIN syntax instead of mixing it with real conditions in where clause. I believe that this should work:
CREATE VIEW no_cat_customers(customer_first_name, customer_last_name) AS
SELECT first_name, last_name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM shipments s
JOIN editions e ON s.isbn = e.isbn
JOIN books b ON b.book_id = e.book_id
WHERE b.title ILIKE '%The Cat in the Hat%'
AND c.c_id = s.c_id
)
If you have datatype errors, cast your columns to appropriate types before comparing them.

Related

On an SQL Select, how do i avoid getting 0 results if I want to query for optional data in another table?

I have a table with Customers which includes their contact person in the helpdesk. I have another table that lists all vacancies of the helpdesk employees - if they are currently sick or on vacation etc.
I need to get the helpdesk contact and the start/end time of their vacation IF there is an entry.
I currently have this (simplified):
SELECT *
FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND dbo.Projects.HDContactID = dbo.Vacations.HDContactID
So if there is a vacation listed in the Vacations table, it works fine, but if there is no vacation at all, this will not return anything - what i want is that if there is no vacation, it simply returns the other data, and ignores the missing data (returns NULL, doesn't return anything, not important)
In any case, I need to get the Customers and Project data, even if the query can't find an entry in the Vacations table. How would I do this? I pretty new to SQL and couldn't find a similar question on this site
EDIT: I'm using SQL Server, currently using HeidiSQL
Try below query:
SELECT * FROM dbo.Customers, dbo.Projects
left join dbo.Vacations on dbo.Projects.HDContactID = dbo.Vacations.HDContactID
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
Use left join as mentioned by #Flying Thunder,
Example of the left join:
SELECT country.country_name_eng, city.city_name, customer.customer_name
FROM customer
LEFT JOIN city ON customer.city_id = city.id
LEFT JOIN country ON city.country_id = country.id;
You can find a nice guide for the joins and SQL here:
https://www.sqlshack.com/learn-sql-join-multiple-tables/
You should be using LEFT JOIN. In fact, you should never be using commas in the FROM clause. That is just archaic syntax and closes the powerful world of JOINs from your queries.
I also recommend using table aliases that are abbreviations of table names. The best are abbreviations for the table names:
SELECT *
FROM dbo.Customers c LEFT JOIN
dbo.Projects p
ON c.CustomerID = p.CustomerID LEFT JOIN
dbo.Vacations v
ON p.HDContactID = v.HDContactID
WHERE c.Phone = $Phone;
Have you try this to skip vacation record if not present like this:
SELECT * FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND (dbo.Vacations.HDContactID IS NULL OR dbo.Projects.HDContactID = dbo.Vacations.HDContactID)

Counting positions in a row oracle

I'm making bookstore database and in the table "Authors" I've got column where i'd like to count books written by author in the table "Books". Table "Books" has foreign key "id_author". I have no idea how to do it, it's something like
SELECT COUNT(*) FROM Books WHERE id_author = "id of chosen author"
What to write in code in place of "id of chosen author"?
How to put it in a row in table "Author"?
You could join on the authors table and query by its columns (e.g., the first and last name):
SELECT COUNT(*)
FROM books b
JOIN author a ON b.id_author = a.id
WHERE a.firstname = 'John' AND a.lastname = 'Doe'
Depending on the application you have, the application might already have the author id. This means that you could just do a
SELECT COUNT(*) FROM Books WHERE id_author = "id of author provided by application"
Although the difference is probably not large compared to Mureinik's answer, this can enhance your performance on large databases as you do not have to do the join between author and book table.
Number of books by author:
select a.author, count(1)
from author a
join books b
on a.id = b.id_author
group by a.author

How to Select foreign key names instead of number in SQL Server

I have the following situation:
One table called cad (id, name, gender, age, fok_professional, fok_agegroup, fok_ativity) and other table called professional (id, name), agegroup (id, desc),
ativity (id, name);
How to select the name value instead number as generally is presented in simple query: For exemple:
SELECT * FROM cad
output is:
id -> 1;
name -> Teste;
gender -> Male;
age -> 22;
fok_professional -> 1;
fok_agegroup -> 4;
fok_ativity -> 2;
instead I would like:
id -> 1;
name -> Teste;
gender -> Male;
age -> 22;
fok_professional -> Administrator;
fok_agegroup -> Age 19 55;
fok_ativity -> Testestetstats;
How to get the values name ?
You want something like this, using SQL JOINs to connect the tables together :
SELECT
cad.id, cad.name, cad.gender, cad.age,
professional.name,
agegroup.desc,
ativity.name
FROM cad INNER JOIN professional ON cad.fok_professional = professional.id
INNER JOIN agegroup ON cad.fok_agegroup = agegroup.id
INNER JOIN ativity ON cad.fok_ativity = ativity.id
You need to use the JOIN statement on tables cad, professionnal, agegroup and activity ON fok_XXX=XXX.id
Assuming you have appropriate permissions on the database that those tables belong to, if you want to be able to be able to just call something as simple as SELECT * FROM sometable to produce that sort of output, you may want to consider creating a view, then joining the two other tables that contain the human-readable rows in your view query.
Luckily for you, SQL Server Management Studio has a simple view designer which can help you in this regard. To access it, expand the database you want to create the view for in the Object Explorer, right-click Views, then click New View. Once you add the three tables you need into the View Designer, it will automatically create the required joins for you, assuming the relationships on each table are set up properly.
If you don't have SSMS, or you're not sure what views are, or why they're useful for your particular situation, you should review the SQL Views topic on TechNet. If you're not sure what joins are, or why tabular relationships are important, then you should consider brushing up on relational database theory a bit.
You need to use inner join between those three tables.
Try this:
SELECT C.ID,
C.NAME,
C.GENDER,
C.AGE,
P.NAME AS [FOK_PROFESSIONAL],
A.DESC AS [FOK_AGEGROUP],
AT.NAME AS [FOK_ATIVITY]
FROM CAD C
INNER JOIN PROFESSIONAL P
ON C.FOK_PROFESSIONAL = P.ID
INNER JOIN AGEGROUP A
ON C.FOK_AGEGROUP = A.ID
INNER JOIN ATIVITY AT
ON C.FOK_ATIVITY = AT.ID
You could use above solutions or you can use the following known as Common Table Expressions (CTEs) for more readability.
WITH C AS
(
SELECT
CAD.id, CAD.name, CAD.gender, CAD.age,
PRO.name,
AG.desc,
AT.name
FROM CAD INNER JOIN professional As PRO ON CAD.fok_professional = PRO.id
INNER JOIN agegroup As AG ON CAD.fok_agegroup = AG.id
INNER JOIN ativity As AT ON CAD.fok_ativity = AT.id
)
SELECT * FROM C;

column ambigously defined error

i have this code
select people_id,first_name
FROM people , PUBAUTHOR_PEOPLE
WHERE people.people_id = PUBAUTHOR_PEOPLE. people_id
and it says column ambigously defined does anybody know why and how to fix that?
It's complaining because both tables have a column called 'people_id', so you have to specify which table to read it from (even though it will be the same because of the join condition), so something like:
SELECT people.people_id,
first_name
FROM people, PUBAUTHOR_PEOPLE
WHERE people.people_id = PUBAUTHOR_PEOPLE.people_id
You should add table name before your coulmn names as below
select people.people_id,people.first_name
FROM people , PUBAUTHOR_PEOPLE
WHERE people.people_id = PUBAUTHOR_PEOPLE. people_id
I recommended you to use aliases as below to make code more readable
select p.people_id,p.first_name
FROM people p, PUBAUTHOR_PEOPLE pp
WHERE p.people_id = pp.people_id
I also recommended you to use joins as below
select p.people_id,p.first_name
FROM people p
join PUBAUTHOR_PEOPLE pp on p.people_id = pp.people_id
It is more readable now, isn't it?

Select all in table where a column in table 1 exists in another table and a second column equals a variable in second table

I know the title is confusing but its the best I could explain it. Basically im developing a cinema listings website for a company which owns two cinemas. So I have a database which has the two tables "Films" and "Listings" with data for both cinemas in them.
I'm trying to select all films and their data for one cinema if the films name shows up in the listings (since the two cinemas share all films but in the table but the may not have the same films showing)
Here is what i have come up with but I run into a problem as when the "SELECT DISTINCT" returns more than one result it obviously cant be matched with the FilmName on tbl Films.
How can i check this value for all FilmNames on tblFilms?
SELECT *
FROM tblFilms
WHERE FilmName = (SELECT DISTINCT FilmName FROM tblListings WHERE Cimema = 1)
use IN if the subquery return multiple values,
SELECT *
FROM tblFILMS
WHERE FilmName IN (SELECT DISTINCT FilmName FROM tblListings WHERE Cimema = 1)
Another way to solve thius is by using JOIN (which I recommend)
SELECT DISTINCT a.*
FROM tblFILMS a
INNER JOIN tblListings b
ON a.FilmName = b.FilmName AND
b.Cimema = 1
for faster query execution, add an INDEX on FilmName on both tables.
If you have your schemas for the tables, that would help.
That said, I believe what you want to look at is the JOIN keyword. (inner/outer/left/etc). That's exactly what JOIN is meant to do (ie your title).