Concatenating two tables distributively - sql

I'm not 100% sure how to phrase the question, but I'm pretty much trying to do this:
say I have two tables:
table a:
a1
a2
and
table b:
b1
b2
I want to combine them and create a table such as:
a1 b1
a1 b2
a2 b1
a2 b2
(for every row in table a, create row number of rows in table b sort of)
I figure I'd be able to do this using a loop of some sort, but I was wondering if there was any way to do this with set logic?

The syntax you're looking for is a cross join:
SELECT a.*, b.*
FROM a
CROSS JOIN b

You don't need any loops.
This is a very simple task in SQL.
You can do:
select a.*, b.*
from a
cross join b
or:
select a.*, b.*
from a
inner join b on (1=1)

No need to loop just simple one line query would work.
SELECT a.*, b.* FROM a,b
Note: By Default it is cross join so no need to define keyword cross join.

Related

Joining on 2 tables but only selecting rows from one of the tables

I have 2 tables with identical names and schema. I would like to join on them, but only select rows from one of the tables. What is a good way to do this? The below query selects the rows from both tables, but I just want table a2 from the other DB.
select a.fkey_id, a2.fkeyid_id, a.otherthing, a2.otherthing from mytable a
inner join otherdb.dbo.mytable a2 on a.fkey_id=a2.fkey_id
I tried using left outer join but since the schemas are identical between the 2 tables this doesn't seem to work.
EDIT: I am only including the "a" table columns in the select to get an idea of what values the rows are returning. I just don't want any rows returned from "a", so I'd like to filter those rows out somehow.
Just take out the references to "a2" columns from the select list.
select a.fkey_id, a.otherthing from mytable a
inner join otherdb.dbo.mytable a2 on a.fkey_id=a2.fkey_id
OR
select a.* from mytable a
inner join otherdb.dbo.mytable a2 on a.fkey_id=a2.fkey_id
Which begs the questions on why you're joining to the other table if you don't want data from it. Is this a filtering method? If so, it would better performance-wise to do an exists.
select a.* from mytable a
WHERE EXISTS (
SELECT 1
FROM otherdb.dbo.mytable a2
WHERE a.fkey_id=a2.fkey_id)
select a.fkey_id
, a.otherthing
from mytable a
WHERE EXISTS (SELECT 1
FROM otherdb.dbo.mytable a2
WHERE a.fkey_id=a2.fkey_id)

Select full outer join from many-to-many relationships

I am trying to do something in MSSQL which I suppose is a fairly simple and common thing in any database with many-to-many relationships. However I seem to always end up with a quite complicated select query, I seem to be repeating the same conditions several times to get the desired output.
The scenario is like this. I have 2 tables (table A and B) and a cross table with foreign keys to the ID columns of A and B. There can only be one unique pair of As and Bs in the crosstable (I guess the 2 foreign keys make up a primary key in the cross table ?!?). Data in the three tables could look like this:
TABLE A TABLE B TABLE AB
ID Type ID Type AID BID
--------------------------------------------------
R Up 1 IN R 3
S DOWN 2 IN T 3
T UP 3 OUT T 5
X UP 4 OUT Z 6
Y DOWN 5 IN
Z UP 6 OUT
Now let's say I select all rows in A of type UP and all rows in B of type OUT:
SELECT ID FROM A AS A1
WHERE Type = 'UP'
(Result: R, T, X, Z)
SELECT ID FROM B AS B1
WHERE Type = 'OUT'
(Result: 3, 4, 6)
What I want now is to fully outer join these 2 sub queries based on the relations listed in AB. Hence I want all IDs in A1 and B1 to be listed at least once:
A.ID B.ID
R 3
T 3
null 4
X null
Z 6
From this results set I want to be able to see:
- Which rows in A1 does not relate to any rows in B1
- Which rows in B1 does not relate to any rows in A1
- Relations between rows in A1 and B1
I have tried a couple of things such as:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN AB ON
A1.ID = AB.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB.BID = B1.ID
This doesn't work, since some of the relations listed in AB are between rows in A1 and rows NOT IN B1 OR between rows in B1 but NOT IN A1.
In other words - I seem to be forced to create a subquery for the AB table also:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN (
SELECT * FROM AB AS AB1
WHERE
AID IN (SELECT ID FROM A WHERE type = 'UP') AND
BID IN (SELECT ID FROM B WHERE type = 'OUT')
) AS AB1 ON
A1.ID = AB1.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB1.BID = B1.ID
That just seems like a rather complicated solution for a seemingly simply problem. Especially when you consider that for A1 and B1 subqueries with more (complex) conditions - possible involving joins to other tables (one-to-many) would require the same temporary joins and conditions to be repeated in the AB1 subquery.
I am thinking that there must be an obvious way to rewrite the above select statements in order to avoid having to repeat the same conditions several times. The solution is probably right there in front me, but I just can't see it.
Any help would be appreciated.
I think you could employ a CTE in this case, like this:
;WITH cte AS (
SELECT A.ID AS AID, A.Type AS AType, B.ID AS BID, B.Type AS BType
FROM A FULL OUTER JOIN AB ON A.ID = AB.AID
FULL OUTER JOIN B ON B.ID = AB.BID)
SELECT AID, BID FROM CTE WHERE AType = 'UP' OR BType = 'OUT'
The advantage of using a CTE is that it will be compiled once. Then you can add additional criteria to the WHERE clause outside the CTE
Check this SQL Fiddle

Inner join Without duplicates, is it possible?

Given these two tables
Table A1 has two rows with the same value 'a'
A1
a
a
Table A2 has two rows with primary key value A,B and they are associated with 'a'
A2
PK col2
A a
B a
What I want is a join of A1 and A2 with this result
a A
a B
Obviously inner join doesn't work here. Is there a way to do this in SQL Server 2008?
You can wipe out the duplicates by using DISTINCT
select distinct
A1.col1,
A2.PK
from
A1
inner join A2
on A1.col1 = A2.col2
If distinct is not restricted
SELECT DISTINCT a.*, b.pk
FROM A1 a
INNER JOIN A2 b ON (a.[test] = b.fk)
There are no joining condition in the post, so we need to go for cross join. I have applied cross join and restrict the duplicate values using distinct.
Select distinct A1.Col1, A2.Pk
From A1 ,A2
"and restrict the duplicate values using distinct."
at least in Postgres 9+ DISTINCT eliminates existing duplicates but not preventing or restricting its appearing.
SELECT DISTINCT A.*
FROM aTable AS A
INNER JOIN
bTable AS B USING(columnId)

filter duplicates in SQL join

When using a SQL join, is it possible to keep only rows that have a single row for the left table?
For example:
select * from A, B where A.id = B.a_id;
a1 b1
a2 b1
a2 b2
In this case, I want to remove all except the first row, where a single row from A matched exactly 1 row from B.
I'm using MySQL.
This should work in MySQL:
select * from A, B where A.id = B.a_id GROUP BY A.id HAVING COUNT(*) = 1;
For those of you not using MySQL, you will need to use aggregate functions (like min() or max()) on all the columns (except A.id) so your database engine doesn't complain.
It helps if you specify the keys of your tables when asking a question such as this. It isn't obvious from your example what the key of B might be (assuming it has one).
Here's a possible solution assuming that ID is a candidate key of table B.
SELECT *
FROM A, B
WHERE B.id =
(SELECT MIN(B.id)
FROM B
WHERE A.id = B.a_id);
First, I would recommend using the JOIN syntax instead of the outdated syntax of separating tables by commas. Second, if A.id is the primary key of the table A, then you need only inspect table B for duplicates:
Select ...
From A
Join B
On B.a_id = A.id
Where Exists (
Select 1
From B B2
Where B2.a_id = A.id
Having Count(*) = 1
)
This avoids the cost of counting matching rows, which can be expensive for large tables.
As usual, when comparing various possible solutions, benchmarking / comparing the execution plans is suggested.
select
*
from
A
join B on A.id = B.a_id
where
not exists (
select
1
from
B B2
where
A.id = b2.a_id
and b2.id != b.id
)

How to make a one to one left outer join?

I was wondering, is there a way to make a kind of one to one left outer join:
I need a join that matches say table A with table B, for each record on table A it must search for its pair on table B, but there exists only 1 record that matches that condition, so when it has found its pair on B, it must stop and continue with the next row at table A.
What I have is a simple LEFT OUTER JOIN.
select * from A left outer join B on A.ID = B.ID order by (NAME) asc
Thanks in advance!
SQL doesn't work this way. In the first place it does not look at things row-by-row. In the second place what defines the record you want to match on?
Assuming you don't really care which row is selcted, something like this might work:
SELECT *
From tableA
left outer join
(select b.* from tableb b1
join (Select min(Id) from tableb group by id) b2 on b1.id - b2.id) b
on a.id = b.id
BUt it still is pretty iffy that you wil get the records you want when there are multiple records with the id in table b.
The syntax you present in your question is correct. There is no difference in the query for joining on a one-to-one relationship than on a one-to-many.