SQL Group By, Distinct confusion - sql

I have a program that is linked to a sql DB, with 2 tables - Customers and SInfo.
Customers has the normal columns:
CUSTOMERID (Primary Key)
FIRSTNAME
LASTNAME
EMAIL
SInfo has details about the Customer:
SSheetID (Primary Key)
CustomerID
SerialNumber
When I use the query builder in .net, I use an inner join to combine the tables, and use a filter to search for a serial number, but I am getting an error when the primary key is shown more than once.
I tried to use distinct, but if one field was different, I would get the error, and GROUP BY is killing me.
So my question is, what would be the best practice if a Customer (CustomerID) has the same Serial Number more than once, but I just want to show that CustomerID once, but still fill out FirstName, LastName, Email?
Any help is appreciated.

Like what Turophile said i think the data in SInfo table got some problem.so what you should do is to clean the table SInfo first or you can try this:
select a.CustomerID,a.SerialNumber,b.FirstName from
(select distinct (CustomerID),SerialNumber from SInfo)a
inner join Customers b on a.CustomerID = b.CustomerID

You can use natural join for this case because when you use natural join, it will match people with the same Customer ID and then use GROUP BY so it will show the name one time.
select first_name, last_name, email
from customer natural join Sinfo
group by customerID, first_name, last_name, email

Various options...
via distinct:
select distinct c.* -- or better: more specific c. columns
from SInfo s
inner join Customers c on c.CUSTOMERID = s.CustomerID
where s.SerialNumber = #serial
via exists:
select *
from Customers c
where exists (
select 1 from SInfo s
where s.CustomerID = c.CUSTOMERID
and s.SerialNumber = #serial)
but as noted in the comments: a unique constraint over SInfo.SerialNumber, or perhaps spanning SInfo.SerialNumber and SInfo.CustomerID - probably makes sense.

Related

Selecting records in SQL table if either JOIN statement is satisfied

I have a table of Companies that I'd like to filter to just the records that are referenced from two other tables, Employees and Contracts.
My initial instinct is that I might be able to do something like this:
SELECT c.*
FROM Companies c
JOIN Employees ON EmployerId = c.Id
JOIN Contracts ON CompanyId = c.Id
However, this selects the records that are referenced from both Employees and Contracts tables.
How do I rewrite this query to match records that are referenced from either table?
I initially put up a buggy version that failed, with LEFT OUTER JOINS. It could have been fixed but it's actually probably more efficient to use WHERE EXISTS e.g.,
SELECT c.*
FROM Companies c
WHERE EXISTS (SELECT EmployerID FROM Employee WHERE EmployerID = c.ID)
OR EXISTS (SELECT CompanyId FROM Contracts WHERE CompanyId = c.ID)
The reason my previous version was buggy, was that it had a join to Employees and Contracts, which meant it would return one row for every one that existed (and indeed, possibly the cross-product of results). Even with a GROUP BY or DISTINCT, it is likely SQL Server would have had to do a lot of work.
The above solution just gets one row per Company, regardless of number of employees or contracts.
You probably need a UNION to get this done
SELECT c.*
FROM Companies c
JOIN Employees ON EmployerId = c.Id
UNION
SELECT c.*
FROM Companies c
JOIN Contracts ON CompanyId = c.Id

Merging two querying

I will introduce my problem with a simple example.
I have two generic queries on the same table, say table 'customers'
Defined 'plushes' a table containing information about in-store plushes, the following first query retrieves for a certain subset of Customers, say first subset, their preferred plush.
WITH preferences
AS
(
SELECT CustomerID,
CustomerName,
City,
Country,
PlushType,
PlushPrice
FROM customers cs
LEFT JOIN plushes ps
ON cs.CustomerID = ps.CustomerID
WHERE cs.CustomerID < 4
)
SELECT CustomerID, PlushType, PlushPrice
FROM preferences
In the same way, defined 'dishes' a table containing world famous dishes, the second query retrieve for a another subset of Customers, say second subset, their preferred dish.
WITH foodPreferences
AS
(
SELECT CustomerID,
CustomerName,
City,
Country,
FoodName,
FoodPrice
FROM customers cs
LEFT JOIN foods fs
ON cs.CustomerID = fs.CustomerID
WHERE fs.FoodName = 'pizza'
)
SELECT CustomerID, FoodName
FROM foodPreferences -- it returns customer 6
What I am searching for, is a query that shows customerID, plushType, plushPrice for the customers of the first OR the second subset, i.e. :
That means, I would like to apply the first query to the first OR a second (deriving from another query) subset.
In other words, I want to run the first query for those customers who love pizza.
I am using OracleDB, with PL/Sql language.
Any idea?
P.s. I know that for the written example the structure of the used queries appears weird. Indeed, I am working with something more complex and I preferred to mirror the structure of the query I have
Added this new answer that is more efficient:
with selected_customers (customerid) as (
select customerid
from customers
where customerid < 4
union
select customerid
from customers
left join foods fs on cs.customerid = fs.customerid
where fs.foodname = 'pizza'
)
select customerid, ps.plushtype, ps.plushprice
from selected_customers cs
left join plushes ps on cs.customerid = ps.customerid;
This query will do:
select customerid, plushtype, plushprice
from customers cs
left join plushes ps on cs.customerid = ps.customerid
where customerid in (
select customerid
from customers
where customerid < 4
)
or customerid in (
select customerid
from customers cs
left join foods fs on cs.customerid = fs.customerid
where fs.foodname = 'pizza'
);

SQL join beween tables with if statement

Say, I have the following two tables:
Table customer:
id, salutation, forename, lastname, companyID
and a table company:
Company_id, Company_name, Company_address
and I want to have an evaluation over all users and their company (if they belong to one)
salutation, forename, lastname, companyName
that would amount basically to a very easy script:
select salutation, forename, lastname, company_name
from customer, company
where companyID=Company_id;
The trouble now is just, that companyID can be null. (A customer doesn't need to be part of a company). And since there is no companyID null entry in the company table and any customer who has no company ID listed is omitted due to the joint statement.
Of couse I could divide it into two scripts one for companyid=null and one for not null and mix them with a UNION command, but is there perhaps something like an if statement?
something like:
select salutation, forename, lastname, placeholder
from customer, company
where
if companyID=null then placeholder=null
else (companyID=Company_id and placeholder=company_name);
?
I know there is a case statement, that can check on the field's value and return something else instead, but is there a way to combine that with a joint to another table?
You are looking for an outer join:
select cu.salutation, cu.forename, cu.lastname, co.company_name
from customer cu
left join company co on cu.companyID = co.Company_id;
In general you should stop using the ancient implicit join syntax in the where clause and use an explicit JOIN operator. That is also the only cross-DBMS way to actually do an outer join (all DBMS that supported some proprietary outer join syntax have deprecated that)
Try this
select salutation, forename, lastname, placeholder
from customer, company
where
(companyID=null and placeholder=null )
OR
(companyID=Company_id and placeholder=company_name);
Use a left join instead of an inner join
select a.salutation, a.forename, a.lastname, a.company_name
from customer a
left outer join company b
on a.companyID=b.companyID;

How To Code a Multi-Join SQL Query And Get Query Results Even When 1 Table Lacks A Joining Row

I have the following query statement:
$query_string = '
SELECT customerID, lastName, firstName, companyName, email, citizenship, primaryLanguage
FROM customers
JOIN citizenships USING(citizenshipID)
JOIN languages USING(languageID)
JOIN paymentMethods USING(customerID)
WHERE customerID = "1"
';
Currently the customers, citizens and languages tables each contain rows and join properly. My query result returns 1 row for customer #1.
The paymentMethods table does not contain any rows at this time. When I add the join syntax for paymentMethods to the query string, my query result returns 0 rows for customer #1.
I want to join on paymentMethods and only return a row from the paymentMethods table when one exists without causing no customer rows to be returned otherwise.
How might I tweak my JOIN syntax to make that happen?
Thank you.
Replace it with LEFT JOIN:
SELECT customerID, lastName, firstName, companyName, email, citizenship, primaryLanguage
FROM customers
JOIN citizenships
USING (citizenshipID)
JOIN languages
USING (languageID)
LEFT JOIN
paymentMethods
USING (customerID)
WHERE customerID = "1"
$query_string = '
SELECT customerID, lastName, firstName, companyName, email, citizenship, primaryLanguage
FROM customers
JOIN citizenships USING(citizenshipID)
JOIN languages USING(primaryLanguageID)
LEFT JOIN paymentMethids USING(customerID)
WHERE customerID = "1"
';

SQL statement to get all customers with no orders

I have a typical Persons table and an Orders table defined in such a way that I can do JOIN query as the following to return Orders for all Persons.
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.id=Orders.Person_id
The question is, how do I write a statement that would return all Persons with NO Orders?
I'm using mysql.
Thank all in advance.
You may want to use LEFT JOIN and IS NULL:
SELECT Persons.LastName, Persons.FirstName
FROM Persons
LEFT JOIN Orders ON Persons.id = Orders.Person_id
WHERE Orders.Person_id IS NULL;
The result of a left join always contains all records of the "left" table (Persons), even if the join-condition does not find any matching record in the "right" table (Orders). When there is no match, the columns of the "right" table will NULL in the result set.
This should work... theres more than one way to do it.
select * from persons where person.id not in (select person_id from orders)
Just for completeness, here is the not exists version:
select * from persons p
where not exists
(select null from orders o where o.person_id = p.id)
You can use left join:
SELECT DISTINCT o.CustomerID from Orders as o
left join Customers as c
on o.CustomerID=c.CustomerID
Question
Find customers who have never made an order.
Output the first name of the customer.
Data
Two tables: Customers and Orders
SELECT first_name
from customers
WHERE first_name not in
(select first_name
from customers
join orders on customers.id=orders.cust_id)