Select unrelated columns from two unrelated tables - sql

Is there an easy way to select two columns from two unrelated tables, in order to use those columns in an insert?
Disclaimer: Ideally, I would never need to do this because the schema would have been set up with a little something called "foreign keys" and "referential integrity". But, it seems as though neither of these concepts existed on the planet on which this schema was created.
Here's a simplified version of what I need to do:
Customer Table
Id Name
------------
1 Eddie
2 Stone
3 Mike
Product Table
Id Name
---------------------
100 Drum sticks
101 Guitar strings
102 Amplifier
Invoice Table
Id CustomerName ProductName
---------------------------------------
1000 Eddie Guitar Strings
1001 Mike Amplifier
In my SQL code, I've got a :CustomerId and a :ProductId, and ideally I'd like to do something like this:
INSERT Invoice (
Id,
CustomerName,
ProductName
)
SELECT
:InvoiceId,
Customer.Name,
Product.Name
FROM
Customer,
Product
WHERE
Customer.CustomerId = :CustomerId
and Product.ProductId = :ProductId
This works fine and dandy if both the Customer and Product records exist, but I need to also cater for the scenario where one of them doesn't exist. (Yes, really.)
The only way I can think to do it is by declaring variables like :CustomerName and :ProductName and pre-populating them outside of the insert statement.
Is there a way to achieve this without going down the extra variables approach?

You could do this:
INSERT Invoice (
Id,
CustomerName,
ProductName
)
SELECT
:InvoiceId,
(
SELECT
Customer.Name
FROM
Customer
WHERE
Customer.CustomerId = :CustomerId
),
(
SELECT
Product.Name
FROM
Product
WHERE
Product.ProductId = :ProductId
)
FROM RDB$DATABASE

Next to the answer provided by Arion, you could use a FULL OUTER JOIN with a join condition that is always true. This only works correctly if both subqueries produce a single row.
SELECT
:InvoiceId,
CustomerName,
ProductName
FROM (
SELECT CustomerName
FROM Customer
WHERE CustomerId = :CustomerId
) a
FULL OUTER JOIN (
SELECT ProductName
FROM Product
WHERE Product.ProductId = :ProductId
) b
ON 1 = 1

Related

SQL JOIN to get the compare two values/row from table "A" and the same row from table "B"

I have two tables with the following rows
Table A (transaction)
Order Seller Customer
1 300 500
Table B (Persons)
PersonID FullName
300 Peter White
500 Scott Bold
I want a result like this
Order Seller Customer FullName (Seller) FullName (customer)
1 300 500 Peter White Scott Bold
I've tried multiple things however which makes more sense is a join a table twice, however I'm getting:
Ambiguous column name
This is SQL Server 2019.
Basically I'm looking to retrieve info from the same table instead of creating additional tables. Is that possible? If yes, how do you do? Thank you in advance.
As #jarlh wrote in comment:
select t.order, t.seller, t.customer, sel.fullname, cust.fullname
from transaction t
join persons sel -- sel is an alias to persons table
on sel.personid = t.seller
join persons cust
on cust.personid = t.customer;
Query with join will return the result as long as both seller and customer exist in persons table -- here it should as source table names transactions :).
I have another form of query it still join table B twice.
This is archaic syntax which I don't recommend but for beginner know the concept of JOIN:
select t.*,B.FullName as FullName (customer) from
(
select A.Order,A.Seller,A.Customer,B.FullName as FullName(Seller)
from A,B where A.Seller=B.PersionID
) t, B where t.Customer=B.PersionID
The proper way of JOIN:
select t.*,B.FullName as FullName (customer) from
(
select A.Order,A.Seller,A.Customer,B.FullName as FullName(Seller)
from A JOIN B ON A.Seller=B.PersionID
) t JOIN B ON t.Customer=B.PersionID
Hoping this can help you.

Select rows from table A where string exists in a similar column in table B or table C

I have 3 different SQL tables that I am working with. Here is what the table look like:
Master Accounts
---------------
CustomerNumber PK
CompanyName
More Columns...
Bill Tos
---------------
MasterCustomerNumber FK
CompanyName
Ship Tos
---------------
MasterCustomerNumber FK
CompanyName
I want to write a MS SQL query that returns all the columns in the Master Accounts table where a company name contains the string 'ama' in any of the three tables.
Here is my current SQL:
SELECT DISTINCT
MA.*
FROM MasterAccounts MA
LEFT JOIN BillTos BT
ON MA.CustNo = BT.MasterCustNo
LEFT JOIN ShipTos ST
ON MA.CustNo = ST.MasterCustNo
WHERE MA.CompanyName LIKE '%ama%' OR BT.CompanyName LIKE '%ama%' OR ST.CompanyName LIKE '%ama%'
My goal is to get all master accounts where a billto or a shipto companyname contains 'ama'.
I read yours comments and maybe this go faster:
select MA.* from
MasterAccounts MA
join
(select CustNo nmb from MasterAccounts where CompanyName LIKE '%ama%'
union
select MasterCustNo nmb from BillTos where CompanyName LIKE '%ama%'
union
select MasterCustNo nmb from ShipTos where CompanyName LIKE '%ama%' ) num on (num.nmb = MA.CustNo)
if you need more companies maybe you use company name in lower case...
*if is small error in query i'm sorry - i read this fast....

Select all customers loyal to one company?

I've got tables:
TABLE | COLUMNS
----------+----------------------------------
CUSTOMER | C_ID, C_NAME, C_ADDRESS
SHOP | S_ID, S_NAME, S_ADDRESS, S_COMPANY
ORDER | S_ID, C_ID, O_DATE
I want to select id of all customers who made order only from shops of one company - 'Samsung' ('LG', 'HP', ... doesn't really matter, it's dynamic).
I've come only with one solution, but I consider it ugly:
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company = "Samsung" )
EXCEPT
( SELECT DISTINCT c_id FROM order JOIN shop USING(s_id) WHERE s_company != "Samsung" );
Same SQL queries, but reversed operator. Isn't there any aggregate method which solves such query better?
I mean, there could be millions of orders(I don't really have orders, I've got something that occurs more often).
Is it efficient to select thousands of orders and then compare them to hundreds of thousands orders which have different company? I know, that it compares sorted things, so it's O( m + n + sort(n) + sort(m) ). But that's still large for millions of records, or isn't?
And one more question. How could I select all customer values (name, address). How can I join them, can I do just
SELECT CUSTOMER.* FROM CUSTOMER JOIN ( (SELECT...) EXCEPT (SELECT...) ) USING (C_ID);
Disclaimer: This question ain't homework. It's preparation for the exam and desire to things more effective. My solution would be accepted at exam, but I like effective programming.
I like to approach this type of question using group by and a having clause. You can get the list of customers using:
select o.c_id
from orders o join
shops s
on o.s_id = o.s_id
group by c_id
having min(s.s_company) = max(s.s_company);
If you care about the particular company, then:
having min(s.s_company) = max(s.s_company) and
max(s.s_company) = 'Samsung'
If you want full customer information, you can join the customers table back in.
Whether this works better than the except version is something that would have to be tested on your system.
How about a query that uses no aggregate functions like Min and Max?
select C_ID, S_ID
from shop
group by C_ID, S_ID;
Now we have a distinct list of customers and all the companies they shopped at. The loyal customers will be the ones who only appear once in the list.
select C_ID
from Q1
group by C_ID
having count(*) = 1;
Join back to the first query to get the company id:
with
Q1 as(
select C_ID, S_ID
from shop
group by C_ID, S_ID
),
Q2 as(
select C_ID
from Q1
group by C_ID
having count(*) = 1
)
select Q1.C_ID, Q1.S_ID
from Q1
join Q2
on Q2.C_ID = Q1.C_ID;
Now you have a list of loyal customers and the one company each is loyal to.

Update multiple row values to same row and different columns

I was trying to update table columns from another table.
In person table, there can be multiple contact persons with same inst_id.
I have a firm table, which will have latest 2 contact details from person table.
I am expecting the firm tables as below:
If there is only one contact person, update person1 and email1. If there are 2, update both. If there is 3, discard the 3rd one.
Can someone help me on this?
This should work:
;with cte (rn, id, inst_id, person_name, email) as (
select row_number() over (partition by inst_id order by id) rn, *
from person
)
update f
set
person1 = cte1.person_name,
email1 = cte1.email,
person2 = cte2.person_name,
email2 = cte2.email
from firm f
left join cte cte1 on f.inst_id = cte1.inst_id and cte1.rn = 1
left join cte cte2 on f.inst_id = cte2.inst_id and cte2.rn = 2
The common table expression (cte) used as a source for the update numbers rows in the person table, partitioned by inst_id, and then the update joins the cte twice (for top 1 and top 2).
Sample SQL Fiddle
I think you don't have to bother yourself with this update, if you rethink your database structure. One great advantage of relational databases is, that you don't need to store the same data several times in several tables, but have one single table for one kind of data (like the person's table in your case) and then reference it (by relationships or foreign keys for example).
So what does this mean for your example? I suggest, to create a institution's table where you insert two attributes like contactperson1 and contactperson2: but dont't insert all the contact details (like email and name), just the primary key of the person and make it a foreign key.
So you got a table 'Person', that should look something like this:
ID INSTITUTION_ID NAME EMAIL
1 100 abc abc#inst.com
2 101 efg efg#xym.com
3 101 ijk ijk#fg.com
4 101 rtw rtw#rtw.com
...
And a table "Institution" like:
ID CONTACTPERSON1 CONTACTPERSON2
100 1 NULL
101 2 3
...
If you now want to change the email adress, just update the person's table. You don't need to update the firm's table.
And how do you get your desired "table" with the two contact persons' details? Just make a query:
SELECT i.id, p1.name, p1.email, p2.name, p2.email
FROM institution i LEFT OUTER JOIN person p1 ON (i.contactperson1 = p1.id)
LEFT OUTER JOIN person p2 ON (i.contactperson2 = p2.id)
If you need this query often and access it like a "table" just store it as a view.

Left Join that always includes null records

I'm using Oracle 11gR2 and I am trying to write a query that returns address data from two tables, CUSTOMERS and LOCATIONS. A given customer may (or may not) have different locations, each with their own address.
I would like to return the address for every customer, and all their locations. For example, if the tables contained data like:
CUSTOMERS
CUSTOMER_ID ADDRESS
1 "New York"
2 "California"
LOCATIONS
CUSTOMER_ID LOCATION_ID ADDRESS
1 1 "New Jersey"
Then I want the results to look like:
CUSTOMER_ID LOCATION_ID ADDRESS
1 "New York"
1 1 "New Jersey"
2 "California"
My first thought was something like this:
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM
CUSTOMERS
LEFT JOIN
LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID)
The problem with that is that when a customer does have locations, it does not return a row with null values for location data, so I don't get a row with the address in the CUSTOMERS table. It gives me something like this:
CUSTOMER_ID LOCATION_ID ADDRESS
1 1 "New Jersey"
2 "California"
It's missing the New York address for customer 1. I tried this...
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM
CUSTOMERS
LEFT JOIN
LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID OR LOCATIONS.CUSTOMER_ID IS NULL)
But it gave me the same results as the first query. Is there a way to return a null record for the second table even when there is a match on the join condition?
You don't need a join here at all:
SELECT customer_id, NULL AS location_id, address
FROM customers
UNION ALL
SELECT customer_id, location_id, address
FROM locations
You can try a full outer join. For example:
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM CUSTOMERS
FULL OUTER JOIN LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID)
If you want to join the two tables even when there is a non match, you will need to use IS NULL on your joined columns.
For example.
Table 1:
CustomerID
CustomerName
.
Table 2:
CustomerID
CustomerEmail
.
Select,
CustomerID,
CustomerName,
ISNULL (CustomerEmail, NULL) AS CustomerEmail
FROM table1
LEFT JOIN table2
ON table1.CustomerID = table2.CustomerID
This wil bring back results with NULL