Join four tables in SQL - sql

I am trying to join four tables, with almost the same data together.
+------------------+------------------+-------------------+-------------------+--+
| tableone.letters | tabletwo.letters | tablethree.leters | tablefour.letters | |
+------------------+------------------+-------------------+-------------------+--+
| 'a' | 'a' | 'a' | 'a' | |
+------------------+------------------+-------------------+-------------------+--+
| 'b' | 'b' | 'b' | 'e' | |
+------------------+------------------+-------------------+-------------------+--+
| 'c' | 'c' | 'c' | 'g' | |
+------------------+------------------+-------------------+-------------------+--+
| 'd' | 'd' | 'e' | | |
+------------------+------------------+-------------------+-------------------+--+
| 'e' | 'f' | | | |
+------------------+------------------+-------------------+-------------------+--+
| 'f' | 'g' | | | |
+------------------+------------------+-------------------+-------------------+--+
| 'g' | 'h' | | | |
+------------------+------------------+-------------------+-------------------+--+
| 'h' | 'i' | | | |
+------------------+------------------+-------------------+-------------------+--+
| 'i' | | | | |
+------------------+------------------+-------------------+-------------------+--+
| 'j' | | | | |
+------------------+------------------+-------------------+-------------------+--+
SELECT DISTINCT tableone.letters, tabletwo.letters, tablethree.letters, tablefour.letters FROM querytesting.tableone
FULL JOIN querytesting.tabletwo
ON tableone.letters = tabletwo.letters
FULL JOIN querytesting.tablethree
ON tabletwo.letters = tablethree.letters
FULL JOIN querytesting.tablefour
ON tablethree.letters = tablefour.letters;
When I join them I get the following result:
+------+------+------+------+--+
| "a" | "a" | "a" | "a" | |
+------+------+------+------+--+
| "b" | "b" | "b" | null | |
+------+------+------+------+--+
| "c" | "c" | "c" | null | |
+------+------+------+------+--+
| "d" | "d" | null | null | |
+------+------+------+------+--+
| "e" | null | null | null | |
+------+------+------+------+--+
| "f" | "f" | null | null | |
+------+------+------+------+--+
| "g" | "g" | null | null | |
+------+------+------+------+--+
| "h" | "h" | null | null | |
+------+------+------+------+--+
| "i" | "i" | null | null | |
+------+------+------+------+--+
| "j" | null | null | null | |
+------+------+------+------+--+
| "k" | null | null | null | |
+------+------+------+------+--+
| "l" | null | null | null | |
+------+------+------+------+--+
| null | null | "e" | "e" | |
+------+------+------+------+--+
| null | null | null | "g" | |
+------+------+------+------+--+
This is not the result I expected. I wanted the 'e' and 'g' in the third and fourth column to line up perfectly with the 'e' and 'g' in the first column.
Is there any way to do that?
Thank you in advance!

Given your explanation all you need to do is FULL JOIN all tables with tableone, though I'm not exactly sure if this is the actual intention specially if all data is only sample. Here:
SELECT DISTINCT tableone.letters, tabletwo.letters, tablethree.letters, tablefour.letters
FROM tableone
FULL JOIN tabletwo
ON tableone.letters = tabletwo.letters
FULL JOIN tablethree
ON tableone.letters = tablethree.letters
FULL JOIN tablefour
ON tableone.letters = tablefour.letters;
See it working here: http://sqlfiddle.com/#!17/acc44/4

Related

Generate random pairs SQL

Suppose we have these two tables.
TABLE1:
|column_1 | ... |
--------------------
| 'a' | ... |
| 'b' | ... |
| 'c' | ... |
| 'd' | ... |
| 'e' | ... |
TABLE_2:
|column_1 | ... |
--------------------
| 1 | ... |
| 2 | ... |
| 3 | ... |
| 4 | ... |
| 5 | ... |
I want to pair all rows of TABLE_1 with some random columns from TABLE_2 where each pair is gonna have a random amount of distinct rows from TABLE_2 (range 1,2,3)
An output could be:
|column_1 | column_2 |
---------------------------
| 'a' | 1 |
| 'a' | 2 |
| 'a' | 5 |
| 'b' | 5 |
| 'c' | 3 |
| 'c' | 4 |
| 'd' | 3 |
| 'e' | 3 |
| 'e' | 5 |
| 'e' | 1 |
JOIN LATERAL
did the thing for me.
SELECT *
FROM TABLE1
LEFT JOIN LATERAL(
SELECT *
FROM TABLE2 LIMIT FLOOR(RANDOM() * 3 + 1)) a
ON TRUE

SQL all rows into one column - no concatenation

I have a table that I need has multiple rows and I to put them all into a new table.
All my rows need to be converted into one row.
+-------+-----------+-------+--------+
| ID | Name | Last | Gender |
+-------+-----------+-------+--------+
| 1 | Person1 | Last1 | M |
| 2 | Person2 | Last2 | F |
| 3 | Person3 | Last3 | M |
| 4 | Person4 | Last4 | F |
+-------+-----------+-------+--------+
I need to convert the above table to the below:
+-------+------------+------------+
| NewID | ColumnName | Value |
+-------+------------+------------+
| 1 | ID | 1 |
| 1 | Name | Person1 |
| 1 | Last | Last1 |
| 1 | Gender | M |
| 2 | ID | 2 |
| 2 | Name | Person2 |
| 2 | Last | Last2 |
| 2 | Gender | F |
| 3 | ID | 3 |
| 3 | Name | Person3 |
| 3 | Last | Last3 |
| 3 | Gender | M |
| 4 | ID | 4 |
| 4 | Name | Person4 |
| 4 | Last | Last4 |
| 4 | Gender | F |
| | | |
+-------+------------+------------+
The most general method is to use union all:
select 'id' as columnname, cast(id as varchar(255)) as value from t union all
select 'name', name as value from t union all
select 'last', last as value from t union all
select 'gender', gender as value from t;
This should work in basically any database, although the cast to a string might vary. Some databases offer other solutions that are more efficient.
Union happy solution.
select 'id' as columnname, id as value from table
union all
select 'name' as columnname, name as value from table
union all
.e
.t
.c

Nested case with multiple sub conditions

I'm having trouble to understand how to nest case statements properly.
(MSSQL Server 2012)
Let's have the following table given.
The Column StatusMissing is what I want to create
+------+--+------+--+------+--+------+--+------+--+------+--+---------------+
| a1 | | a2 | | a3 | | b1 | | c1 | | d2 | | StatusMissing |
+------+--+------+--+------+--+------+--+------+--+------+--+---------------+
| OK | | OK | | OK | | OK | | OK | | OK | | AllOK |
| NULL | | NULL | | OK | | OK | | OK | | OK | | As |
| OK | | NULL | | OK | | OK | | OK | | OK | | As |
| OK | | OK | | NULL | | OK | | OK | | OK | | As |
| OK | | OK | | OK | | NULL | | OK | | OK | | B |
| OK | | OK | | OK | | OK | | NULL | | OK | | C |
| OK | | OK | | OK | | OK | | OK | | NULL | | D |
| NULL | | OK | | OK | | NULL | | NULL | | OK | | ABC |
| NULL | | OK | | OK | | OK | | NULL | | NULL | | ACD |
| NULL | | OK | | OK | | NULL | | OK | | NULL | | ABD |
| NULL | | OK | | OK | | NULL | | NULL | | NULL | | ABCD |
| NULL | | OK | | OK | | OK | | NULL | | NULL | | ACD |
| OK | | OK | | OK | | NULL | | NULL | | OK | | BC |
| OK | | OK | | OK | | OK | | OK | | OK | | AllOK |
| OK | | NULL | | OK | | OK | | NULL | | OK | | AC |
| OK | | OK | | OK | | NULL | | OK | | NULL | | BD |
| OK | | OK | | OK | | OK | | NULL | | NULL | | CD |
+------+--+------+--+------+--+------+--+------+--+------+--+---------------+
First, to understand the concept of nesting I simplified the table:
+------+--+------+--+------+
| a1 | | a2 | | b1 |
+------+--+------+--+------+
| OK | | OK | | OK |
| OK | | OK | | NULL |
| OK | | NULL | | OK |
| NULL | | OK | | OK |
| NULL | | NULL | | OK |
| NULL | | OK | | NULL |
| OK | | NULL | | NULL |
+------+--+------+--+------+
These attempts lead to these failures.
Query1
SELECT a1, a2, b1 'StatusMissing' =
CASE
WHEN a1 IS NULL
THEN
CASE
WHEN a1 IS NULL
THEN
CASE
WHEN b1 IS NULL
THEN 'AB'
END
ELSE 'A'
END
WHEN b1 IS NULL
THEN 'B'
ELSE 'AllOK'
END
FROM Table;
Result1:
+------+--+------+--+------+--+---------------+
| a1 | | a2 | | b1 | | StatusMissing |
+------+--+------+--+------+--+---------------+
| OK | | OK | | OK | | AllOK |
| OK | | OK | | NULL | | B |
| OK | | NULL | | OK | | AllOK |
| NULL | | OK | | OK | | NULL |
| NULL | | NULL | | OK | | NULL |
| NULL | | OK | | NULL | | AB |
| OK | | NULL | | NULL | | B |
+------+--+------+--+------+--+---------------+
Query2 (Else as main)
SELECT a1, a2, b1, 'Status' =
CASE
WHEN a1 IS NOT NULL AND a2 IS NOT NULL AND b1 IS NOT NULL
THEN 'AllOK!'
ELSE
CASE
WHEN a2 IS NOT NULL OR a2 IS NOT NULL
THEN
CASE
WHEN b1 IS NULL
THEN 'AB'
END
WHEN b1 IS NULL
THEN 'B'
ELSE 'A'
END
END
FROM Table;
Result2
+------+--+------+--+------+--+---------------+
| a1 | | a2 | | b1 | | StatusMissing |
+------+--+------+--+------+--+---------------+
| OK | | OK | | OK | | AllOK |
| OK | | OK | | NULL | | AB |
| OK | | NULL | | OK | | A |
| NULL | | OK | | OK | | NULL |
| NULL | | NULL | | OK | | A |
| NULL | | OK | | NULL | | AB |
| OK | | NULL | | NULL | | B |
+------+--+------+--+------+--+---------------+
What the hell am I doing wrong?
I'm quite new to SQL, so if there is a proper function to do this I would appreciate the info!
EDIT:
If something like this would be possible in SQL i mean:
Column StatusMissing = ' missing'
If(a1 == NULL) { StatusMissing += 'A'}
EDIT2:
The column StatusMissing IS NOT THERE!
I want to create it using the SQL statements like below.
SELECT .... Status =
So basically I only have A1,A2,B1 (in the simple table). Please don't get confused with the first Table. It's only there to SHOW HOW IT SHOULD look like.
For the simplified table, assuming data type to be nvarchar.
Try using UPDATE-
UPDATE [dbo].[StatusMissing]
SET result='';
UPDATE [dbo].[StatusMissing]
SET result= CONCAT(result , 'A')
WHERE a1 is null or a2 is null;
UPDATE [dbo].[StatusMissing]
SET result= CONCAT(result , 'B')
WHERE b1 is null ;
UPDATE [dbo].[StatusMissing]
SET result= 'AllOK'
WHERE result ='';
This can be done in one step as well.
I might suggest that you make two small modifications to your output:
Instead of "As", just say "A".
Instead of "AllOK", just leave the field blank.
With these modifications, the rules are pretty easy:
select t.*,
((case when a1 is null or a2 is null or a3 is null then 'A' else '' end) +
(case when b1 is null then 'B' else '' end) +
(case when c1 is null then 'C' else '' end) +
(case when d1 is null then 'D' else '' end)
) as StatusMissing
from table t;
If you do want your version, a subquery is perhaps the easiest way:
select t. . . .,
(case when StatusMissing = '' then 'AllOK'
when StatusMissing = 'A' then 'As'
else StatusMissing
end) as StatusMissing
from (select t.*,
((case when a1 is null or a2 is null or a3 is null then 'A' else '' end) +
(case when b1 is null then 'B' else '' end) +
(case when c1 is null then 'C' else '' end) +
(case when d1 is null then 'D' else '' end)
) as StatusMissing
from table t
) t
You can play with COALESCE and a couple of CASE conditions
SELECT a1,
a2,
a3,
b1,
c1,
d2,
COALESCE(
CASE WHEN
b1 = 'OK'
AND c1 = 'OK'
AND d2 = 'OK'
AND (a1 IS NULL OR a2 IS NULL OR a3 is NULL)
THEN 'As'
ELSE ''
END,
CASE WHEN
(a1 IS NULL OR a2 IS NULL or a3 is NULL)
THEN 'A'
END
+ CASE WHEN
b1 IS NULL
THEN 'B'
ELSE ''
END
+ CASE WHEN
c1 IS NULL
THEN 'C'
ELSE ''
END
+ CASE WHEN
d2 IS NULL
THEN 'D'
ELSE ''
END,
'AllOK') AS 'StatusMissing'
FROM Table;

Select 5 of each distinct value

I have the following table in PostgreSQL:
| a | b | c |
===================
| 'w' | 2 | 3 |
| 'w' | 7 | 2 |
| 'w' | 8 | 1 |
| 'w' | 3 | 6 |
| 'w' | 0 | 8 |
| 'w' | 2 | 9 |
| 'w' | 2 | 9 |
| 'z' | 4 | 9 |
| 'z' | 0 | 9 |
| 'z' | 0 | 8 |
| 'z' | 3 | 6 |
| 'z' | 2 | 7 |
| 'z' | 3 | 1 |
| 'z' | 3 | 2 |
| 'z' | 3 | 3 |
I want to select all records, but limit them to 5 records for each distinct value in column a.
So the result would look like:
| a | b | c |
===================
| 'w' | 2 | 3 |
| 'w' | 7 | 2 |
| 'w' | 8 | 1 |
| 'w' | 3 | 6 |
| 'w' | 0 | 8 |
| 'z' | 4 | 9 |
| 'z' | 0 | 9 |
| 'z' | 0 | 8 |
| 'z' | 3 | 6 |
| 'z' | 2 | 7 |
What is the most effecient way to achieve that in RoR? Thanks!
you can use row_number, but you have to specify order or you will get unpredictable resutls
with cte as (
select
*,
row_number() over(partition by a order by b, c) as row_num
from table1
)
select a, b, c
from cte
where row_num <= 5

Converting MySQL Resultset from Rows to Columns

I have output from a select like this:
04:47:37> select * from attributes left outer join trailer_attributes on attributes.id = trailer_attributes.attribute_id;
+----+--------------+----------+-----------+------------+--------------+-----------------+
| id | name | datatype | list_page | trailer_id | attribute_id | attribute_value |
+----+--------------+----------+-----------+------------+--------------+-----------------+
| 1 | Make | text | 1 | 1 | 1 | Apple |
| 1 | Make | text | 1 | 2 | 1 | sdfg |
| 2 | Year | number | 1 | 1 | 2 | 2009 |
| 2 | Year | number | 1 | 2 | 2 | sdfg |
| 3 | Type | text | 0 | 1 | 3 | iPhone |
| 3 | Type | text | 0 | 2 | 3 | sdfg |
| 4 | Axles | text | 0 | 1 | 4 | asdf |
| 4 | Axles | text | 0 | 2 | 4 | sdfg |
| 7 | Size | text | 0 | 1 | 7 | asd1 |
| 7 | Size | text | 0 | 2 | 7 | sdfg |
| 8 | Frame | text | 0 | 1 | 8 | |
| 8 | Frame | text | 0 | 2 | 8 | sdfg |
| 9 | Height | text | 0 | 1 | 9 | |
| 9 | Height | text | 0 | 2 | 9 | sdfg |
| 10 | Dollies | text | 0 | 1 | 10 | |
| 10 | Dollies | text | 0 | 2 | 10 | sdfg |
| 11 | Tires/Wheels | text | 0 | 1 | 11 | |
| 11 | Tires/Wheels | text | 0 | 2 | 11 | sdfg |
| 12 | Condition | text | 1 | 1 | 12 | New |
| 12 | Condition | text | 1 | 2 | 12 | sdfg |
| 13 | Title | text | 0 | 1 | 13 | |
| 13 | Title | text | 0 | 2 | 13 | sdfg |
+----+--------------+----------+-----------+------------+--------------+-----------------+
I want to convert it to something more along the lines of:
id, Make, Year, Type, Axles, Size, Frame (etc)
1, Apple, 2009, iPhone, .....
2, sdfg, sdfg, sdfg, .....
Any suggestions?
Mmmm...EAVs. One of the many reasons to avoid EAVs (entity-attribute_value) is that they are harder to report and query against. However, if the attributes you want are known ahead of time, you can do something like:
Select id
, Min( Case When name = 'Make' Then attribute_value End ) As Make
, Min( Case When name = 'Year' Then attribute_value End ) As Year
, Min( Case When name = 'Type' Then attribute_value End ) As Type
, Min( Case When name = 'Axles' Then attribute_value End ) As Axles
, Min( Case When name = 'Size' Then attribute_value End ) As Size
, Min( Case When name = 'Frame' Then attribute_value End ) As Frame
, ...
From attributes
Where name In('Make','Year','Type','Axles','Size','Frame',....)
Group By id
Now, MySQL, does have a GROUP_CONCAT which will let you concatenate multiple values for the same attribute into a list if you allow that (e.g. if an entity can have multiple Make attributes).
This may not be an option for you, but ideally you should convert each attribute into a column of the main table. Relational databases are designed to handle attributes as columns, not rows. Therefore they perform much better when you use them like that, and the SQL becomes much simpler too.