t-SQL cartesian production of several tables - sql

I would like to get a cartesian product of several tables in SQL (which are actually only one column, so no common key). For example:
TABLE A
Robert
Pierre
Samuel
TABLE B
Montreal
Chicago
TABLE C
KLM
AIR FRANCE
FINAL TABLE (CROSS PRODUCT)
Robert | Montreal | KLM
Pierre | Montreal | KLM
Samuel | Montreal | KLM
Robert | Chicago | KLM
Pierre | Chicago | KLM
Samuel | Chicago | KLM
Robert | Montreal | AIR FRANCE
Pierre | Montreal | AIR FRANCE
Samuel | Montreal | AIR FRANCE
Robert | Chicago | AIR FRANCE
Pierre | Chicago | AIR FRANCE
Samuel | Chicago | AIR FRANCE
I tried CROSS JOIN, but I couldn't find an example with multiple tables. Is the only way to do it is nesting? What if we have 15 tables to join that way... it creates a very long code.
Thank you!

You would simply use:
select *
from a cross join b cross join c;
Do note that if any of the tables are empty (i.e. no rows), you will get no results.

Related

SQL - UNION vs NULL functions. Which is better?

I have three tables: ACCT, PERS, ORG. Each ACCT is owned by either a PERS or ORG. The PERS and ORG tables are very similar and so are all of their child tables, but all PERS and ORG data is separate.
I'm writing a query to get PERS and ORG information for each account in ACCT and I'm curious what the best method of combining the information is. Should I use a series of left joins and NULL functions to fill in the blanks, or should I write the queries separately and use UNION to combine?
I've already written separate queries for PERS ACCT's and another for ORG ACCT's and plan on using UNION. My question more pertains to best practice in the future.
I'm expecting both to give me my desired my results, but I want to find the most efficient method both in development time and run time.
EDIT: Sample Table Data
ACCT Table:
+---------+---------+--------------+-------------+
| ACCTNBR | ACCTTYP | OWNERPERSNBR | OWNERORGNBR |
+---------+---------+--------------+-------------+
| 555001 | abc | 3010 | |
| 555002 | abc | | 2255 |
| 555003 | tre | 5125 | |
| 555004 | tre | 4485 | |
| 555005 | dsa | | 6785 |
+---------+---------+--------------+-------------+
PERS Table:
+---------+--------------+---------------+----------+-------+
| PERSNBR | PHONE | STREET | CITY | STATE |
+---------+--------------+---------------+----------+-------+
| 3010 | 555-555-5555 | 1234 Main St | New York | NY |
| 5125 | 555-555-5555 | 1234 State St | New York | NY |
| 4485 | 555-555-5555 | 6542 Vine St | New York | NY |
+---------+--------------+---------------+----------+-------+
ORG Table:
+--------+--------------+--------------+----------+-------+
| ORGNBR | PHONE | STREET | CITY | STATE |
+--------+--------------+--------------+----------+-------+
| 2255 | 222-222-2222 | 1000 Main St | New York | NY |
| 6785 | 333-333-3333 | 400 4th St | New York | NY |
+--------+--------------+--------------+----------+-------+
Desired Output:
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
| ACCTNBR | ACCTTYP | OWNERPERSNBR | OWNERORGNBR | PHONE | STREET | CITY | STATE |
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
| 555001 | abc | 3010 | | 555-555-5555 | 1234 Main St | New York | NY |
| 555002 | abc | | 2255 | 222-222-2222 | 1000 Main St | New York | NY |
| 555003 | tre | 5125 | | 555-555-5555 | 1234 State St | New York | NY |
| 555004 | tre | 4485 | | 555-555-5555 | 6542 Vine St | New York | NY |
| 555005 | dsa | | 6785 | 333-333-3333 | 400 4th St | New York | NY |
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
Query Option 1: Write 2 queries and use UNION to combine them:
select a.acctnbr, a.accttyp, a.ownerpersnbr, a.ownerorgnbr, p.phone, p.street, p.city, p.state
from acct a
inner join pers p on p.persnbr = a.ownerpersnbr
UNION
select a.acctnbr, a.accttyp, a.ownerpersnbr, a.ownerorgnbr, o.phone, o.street, o.city, o.state
from acct a
inner join org o on o.orgnbr = a.ownerorgnbr
Option 2: Use NVL() or Coalesce to return a single data set:
SELECT a.acctnbr,
a.accttyp,
NVL(a.ownerpersnbr, a.ownerorgnbr) Owner,
NVL(p.phone, o.phone) Phone,
NVL(p.street, o.street) Street,
NVL(p.city, o.city) City,
NVL(p.state, o.state) State
FROM
acct a
LEFT JOIN pers p on p.persnbr = a.ownerpersnbr
LEFT JOIN org o on o.orgnbr = a.ownerorgnbr
There are way more fields in each of the 3 tables as well as many more PERS and ORG tables in my actual query. Is one way better (faster, more efficient) than another?
That depends, on what you consider "better".
Assuming, that you will always want to pull all rows from ACCT table, I'd say to go for the LEFT OUTER JOIN and no UNION. (If using UNION, then rather go for UNION ALL variant.)
EDIT: As you've already shown your queries, mine is no longer required, and did not match your structures. Removing this part.
Why LEFT JOIN? Because with UNION you'd have to go through ACCT twice, based on "parent" criteria (whether separate or done INNER JOIN criteria), while with plain LEFT OUTER JOIN you'll probably get just one pass through ACCT. In both cases, rows from "parents" will most probably be accessed based on primary keys.
As you are probably considering performance, when looking for "better", as always: Test your queries and look at the execution plans with adequate and fresh database statistics in place, as depending on the data "layout" (histograms, etc.) the "better" may be something completely different.
I think you misunderstand what a Union does versus a join statement. A union takes the records from multiple tables, generally similar or the same structure and combines them into a single resultset. It is not meant to combine multiple dissimilar tables.
What I am seeing is that you have two tables PERS and ORG with some of the same data in it. In this case I suggest you union those two tables and then join to ACCT to get the sample output.
In this case to get the output as you have shown you would want to use Outer joins so that you don't drop any records without a match. That will give you nulls in some places but most of the time that is what you want. It is much easier to filter those out later.
Very rough sample code.
SELECT a.*, b.*
from Acct as a
FULL OUTER JOIN (
Select * from PERS UNION Select * from ORG
) as b
ON a.ID = b.ID

Flattening a One to Many Relationship with SQL Query

I have a very simple data set that I would like to be able to query and get the results as a single record.
Members Table
ID | FirstName | LastName | HeroName
42 | Bruce | Wayne | Batman
1337 | Bruce | Banner | Hulk
1033 | Clark | Kent | Newspaper Boy
Skills Tables
ID | Skill
42 | Martial Arts
42 | Engineering
42 | Intimidation
1337 | Anger Management
1337 | Thermo Nuclear Dynamics
1033 | NULL
I want the result to be
ID | FirstName | LastName | HeroName | Skill1 | Skill2 | Skill3 | ... | Skilln
42 Bruce | Wayne | Batman | Martial Arts | Engineering | Intimidation
The query I have so far is
SELECT m.ID, m.FirstName, m.LastName, m.HeroName, s.Skill
FROM Members m
JOIN Skills s
ON m.ID = s.ID
WHERE m.ID = 42 and s.Skill IS NOT NULL
which returns
ID | FirstName | LastName | HeroName | Skill
42 | Bruce | Wayne | Batman | Martial Arts
42 | Bruce | Wayne | Batman | Engineering
42 | Bruce | Wayne | Batman | Intimidation
Short of iterating over the results and only extracting the fields I want is there a way to return this as a single record? I've seen topics on PIVOT, and XmlPath but from what I've read neither of these does quite what I want it to. I'd like an arbitrary number of Skills to be returned and no nulls are returned.
EDIT:
The problem with PIVOT is that it will turn one of the rows into a column header. If There is a way to fill in a generic column header than it might work.

Select one of each

How can I get all the countries from the DB, from this table:
city | country | info
Jerusalem | Israel | Capital
Tel Aviv | Israel |
New York | USA | Biggest
Washington DC | USA | Capital
Berlin | Germany | Capital
How can I get, using SQL, the countries only: Israel, USA, Germany?
Which database server are you using?
Assuming that the top row is the column name and you are using MySQL then you should be able to just do
"SELECT distinct(country) FROM <table-name>;"
This is probably in the documentation for the database software that you are using.

How to apply combinatorics on pairs in sql database?

I want to perform some kind of combinatorics from a database.
my database table:
start | end | costs | date
____________________________________
berlin | Moscow | 100 | 2014-12-10
berlin | paris | 200 | 2014-12-13
Moscow | berlin | 150 | 2014-12-20
Moscow | berlin | 100 | 2014-12-11
Possible pairs are all start-end combinations that have an equal combination ofend-start.
In my table, this applies to berlin-moscow and moscow-berlin.
I want to compute "roundtrips" going from one city to another, and returning later to the same startcity.
The resulting table I want to achieve would be:
start | end | costs | away | wayback
berlin | moscow | 250 | 2014-12-10 | 2014-12-20
berlin | moscow | 200 | 2014-12-10 | 2014-12-11
(this implies that when starting in berlin and going to moscow, the wayback will be moscow-berlin).
Is that possible with database queries?
First of all: how could I selfjoin a tables and get all distinct start-end pairs?
As a comment, i would advise you to not use reserved words as End for user defined objects as tables or columns.
SELECT F.Start,
F."end",
F.costs + R.costs AS costs,
F.date AS away,
R.date AS wayback
FROM Table1 F
JOIN Table1 R
ON F."end" = R.Start
AND R.date>F.date
AND F.start = R."end"
SQL Fiddle Demo

Zend Framework: How to combine three tables in one query using Joins?

I have three tables like this:
Person table:
person_id | name | dob
--------------------------------
1 | Naveed | 1988
2 | Ali | 1985
3 | Khan | 1987
4 | Rizwan | 1984
Address table:
address_id | street | city | state | country
----------------------------------------------------
1 | MAJ Road | Karachi | Sindh | Pakistan
2 | ABC Road | Multan | Punjab | Pakistan
3 | XYZ Road | Riyadh | SA | SA
Person_Address table:
person_id | address_id
----------------------
1 | 1
2 | 2
3 | 3
Now I want to get all records of Person_Address table but also with their person and address records like this by one query:
person_id| name | dob | address_id | street | city | state | country
----------------------------------------------------------------------------------
1 | Naveed | 1988 | 1 | MAJ Road | Karachi | Sindh | Pakistan
2 | Ali | 1985 | 2 | ABC Road | Multan | Punjab | Pakistan
3 | Khan | 1987 | 3 | XYZ Road | Riyadh | SA | SA
How it is possible using zend? Thanks
The reference guide is the best starting point to learn about Zend_Db_Select. Along with my example below, of course:
//$db is an instance of Zend_Db_Adapter_Abstract
$select = $db->select();
$select->from(array('p' => 'person'), array('person_id', 'name', 'dob'))
->join(array('pa' => 'Person_Address'), 'pa.person_id = p.person_id', array())
->join(array('a' => 'Address'), 'a.address_id = pa.address_id', array('address_id', 'street', 'city', 'state', 'country'));
It's then as simple as this to fetch a row:
$db->fetchRow($select);
In debugging Zend_Db_Select there's a clever trick you can use - simply print the select object, which in turn invokes the toString method to produce SQl:
echo $select; //prints SQL
I'm not sure if you're looking for SQL to do the above, or code using Zend's facilities. Given the presence of "sql" and "joins" in the tags, here's the SQL you'd need:
SELECT p.person_id, p.name, p.dob, a.address_id, street, city, state, country
FROM person p
INNER JOIN Person_Address pa ON pa.person_id = p.person_id
INNER JOIN Address a ON a.address_id = pa.address_id
Bear in mind that the Person_Address tells us that there's a many-to-many relationship between a Person and an Address. Many Persons may share an Address, and a Person may have more than one address.
The SQL above will show ALL such relationships. So if Naveed has two Address records, you will have two rows in the result set with person_id = 1.