SQL JOIN 3 tables, unique rows, calculation across tables - sql

I have 3 tables. They have the same columns. I need to merge them into one table, such that I only have unique rows, the amount columns needs to be computed across the 3 tables.
e.g.
Table 1
Name1, 2, 100.00
Name5, 3, 25.00
Table 2
Name1, 2, 50.00
Table 3
Name1, 2, 60.00
Desired Result:
Name1, 2, 90.00 i.e. calculated as (100-60+50)
Name5, 3, 25.00
Any ideas? I've tried union but that doesn't calculate for me or show unique rows....

In this solution, I simply took the Min value of the second column since you did not specify how it should be combined across the three tables.
Select Z.Name, Min( SomeInt ) As MinSomeInt
, Sum( SomeDecimal ) As TotalSomeDecimal
From (
Select Name, SomeInt, SomeDecimal
From Table1
Union All
Select Name, SomeInt, SomeDecimal
From Table2
Union All
Select Name, SomeInt, SomeDecimal * -1
From Table3
) As Z
Group By Z.Name

Pretty clear question. Use UNION ALL to combine the tables into one queryable thing, then use GROUP BY to combine rows that share a value.
SELECT Col1, SUM(Col2) as Col2
FROM
(
SELECT Col1, Col2
FROM Table1
UNION ALL
SELECT Col1, Col2
FROM Table2
UNION ALL
SELECT Col1, Col2
FROM Table3
) as sub
GROUP BY Col1

Related

Selecting distinct values within a a group

I want to select distinct values of one variable within a group defined by another variable. What is the easiest way?
My first thought was to combine group by and distinct but it does not work. I tried something like:
select distinct col2, col1 from myTable
group by col1
I have looked at this one here but can't seem to solve my problem
Using DISTINCT along with GROUP BY in SQL Server_
Table example
If your requirement is to pick distinct combinations if col1 and COL2 then no need to group by just use
SELECT DISTINCT COL1, COL2 FROM TABLE1;
But if you want to group by then automatically one record per group is displayed by then you have to use aggregate function of one of the columns i.e.
SELECT COL1, COUNT(COL2)
FROM TABLE1 GROUP BY COL1;
no need group by just use distinct
select distinct col2, col1 from myTable
create table t as
with inputs(val, id) as
(
select 'A', 1 from dual union all
select 'A', 1 from dual union all
select 'A', 2 from dual union all
select 'B', 1 from dual union all
select 'B', 2 from dual union all
select 'C', 3 from dual
)
select * from inputs;
The above creates your table and the below is the solution (12c and later):
select * from t
match_recognize
(
partition by val
order by id
all rows per match
pattern ( a {- b* -} )
define b as val = a.val and id = a.id
);
Output:
Regards,
Ranagal

Oracle SQL How to find duplicate values in different columns?

I have a set of rows with many columns. For example,
ID | Col1 | Col2 | Col3 | Duplicate
------------------------------------
81 | 101 | 102 | 101 | YES
82 | 101 | 103 | 104 | NO
I need to calculate the "Duplicate" column. It is duplicate because it has the same value in Col1 and Col3. I know there is the LEAST function, which is similar to the MIN function but with columns. Does something similar to achieve this exists?
The approach I have in mind is to write all possible combinations in a case like this:
SELECT ID, col1, col2, col3,
CASE WHEN col1 = col2 or col1 = col3 or col2 = col3 then 1 else 0 end as Duplicate
FROM table
But, I wish to avoid that, since I have too many columns in some cases, and is very prone to errors.
What is the best way to solve this?
Hmmm. You are looking for within-row duplicates. This is painful. More recent versions of Oracle support lateral joins. But for just a handful of non-NULL columns, you can do:
select id, col1, col2, col3,
(case when col1 in (col2, col3) or col2 in (col3) then 1 else 0 end) as Duplicate
from t;
For each additional column, you need to add one more in comparison and update the other in-lists.
Something like this... note that in the lateral clause we still need to unpivot, but that is one row at a time - resulting in possibly much faster execution than simple unpivot and standard aggregation.
with
input_data ( id, col1, col2, col3 ) as (
select 81, 101, 102, 101 from dual union all
select 82, 101, 103, 104 from dual
)
-- End of simulated input data (for testing purposes only).
-- Solution (SQL query) begins BELOW THIS LINE.
select i.id, i.col1, i.col2, i.col3, l.duplicates
from input_data i,
lateral ( select case when count (distinct val) = count(val)
then 'NO' else 'YES'
end as duplicates
from input_data
unpivot ( val for col in ( col1, col2, col3 ) )
where id = i.id
) l
;
ID COL1 COL2 COL3 DUPLICATES
-- ---- ---- ---- ----------
81 101 102 101 YES
82 101 103 104 NO
You can do this by unpivoting and then counting the distinct values per id and checking if it equals the number of rows for that id. Equal means there are no duplicates. Then left join this result to the original table to caclulate the duplicate column.
SELECT t.*,
CASE WHEN x.id IS NOT NULL THEN 'Yes' ELSE 'No' END AS duplicate
FROM t
LEFT JOIN
(SELECT id
FROM
(SELECT *
FROM t
unpivot (val FOR col IN (col1,col2,col3)) u
) t
GROUP BY id
HAVING count(*)<>count(DISTINCT val)
) x ON x.id=t.id
The best way† is to avoid storing repeating groups of columns. If you have multiple columns that essentially store comparable data (i.e. a multi-valued attribute), move the data to a dependent table, and use one column.
CREATE TABLE child (
ref_id INT,
col INT
);
INSERT INTO child VALUES
(81, 101), (81, 102), (81, 101),
(82, 101), (82, 103), (82, 104);
Then it's easier to find cases where a value occurs more than once:
SELECT id, col, COUNT(*)
FROM child
GROUP BY id, col
HAVING COUNT(*) > 1;
If you can't change the structure of the table, you could simulate it using UNIONs:
SELECT id, col1, COUNT(*)
FROM (
SELECT id, col1 AS col FROM mytable
UNION ALL SELECT id, col2 FROM mytable
UNION ALL SELECT id, col3 FROM mytable
... for more columns ...
) t
GROUP BY id, col
HAVING COUNT(*) > 1;
† Best for the query you are trying to run. A denormalized storage strategy might be better for some other types of queries.
SELECT ID, col1, col2,
NVL2(NULLIF(col1, col2), 'Not duplicate', 'Duplicate')
FROM table;
If you want to compare more than 2 columns can implement same logic with COALESCE
I think you want to use fresh data that doesnot contains any duplicate values inside table if it right then use SELECT DISTINCT statement like
SELECT DISTINCT * FROM TABLE_NAME
It will conatins duplicate free data,
Note: It will also applicable for a particular column like
SELECT DISTINCT col1 FROM TABLE_NAME

How to pivot rows without grouping, counting, averaging

I am reworking some tables from a screwed up database. A few of the tables had the same data with different table names, and each one of them also had similar data but different column names. Anyway, this is a weird request but this has to be down like this.
I need to pivot rows up to simulate one row so I can create one record from two different tables.
I have attached a photo. The table on the left will pull a single row and the table on the left will supply 1 - n rows based on the id from the left table. I need to pivot the rows up to simulate one row and create one record with the two results.
From my checking online the pivot seems to be the way to go but it seems to want me to group or do some type of aggregating.
What is the best way to go about doing this?
table1 ---Produces one row
table1id | col1 | col2 | col3
1 Wow Wee Zee
table2 ---Produces 1 - n rows
table2id | table1id | col1 | col2 | col3
1 1 sock cloth sup
2 1 bal baa zak
3 1 x y fooZ
needs to look like this (the below is not column names, they're the result set)
Woo,wee,zee,sock,cloth,sup,bla,baaa,zak,x,y,fooZ
If using MySQL:
SELECT a.table1id, GROUP_CONCAT(a.col) AS col_values
FROM
(
SELECT table1id, col1 col FROM table1 UNION ALL
SELECT table1id, col2 FROM table1 UNION ALL
SELECT table1id, col3 FROM table1 UNION ALL
SELECT table1id, col1 FROM table2 UNION ALL
SELECT table1id, col2 FROM table2 UNION ALL
SELECT table1id, col3 FROM table2
) a
GROUP BY a.table1id
SQLFiddle Demo
If using SQL-Server:
SELECT a.table1id, b.colnames
FROM table1 a
CROSS APPLY
(
SELECT STUFF((
SELECT ',' + aa.col
FROM
(
SELECT table1id, col1 col FROM table1 UNION ALL
SELECT table1id, col2 FROM table1 UNION ALL
SELECT table1id, col3 FROM table1 UNION ALL
SELECT table1id, col1 FROM table2 UNION ALL
SELECT table1id, col2 FROM table2 UNION ALL
SELECT table1id, col3 FROM table2
) aa
WHERE aa.table1id = a.table1id
FOR XML PATH('')
), 1, 1, '') AS colnames
) b
SQLFiddle Demo

SQL move data from rows to cols

Sorry for the bad title - I simply do not know what to call the thing I want to do.
Here it goes:
In MS SQL Server 2008
I have a temp table with 4000+ rows created with the WITH statement looking like this:
ID (varchar) DATE (int)
AB1135000097 | 20151221
AB1135000097 | 20160119
AB1135000097 | 20160219
AB1135001989 | 20120223
AB1135001989 | 20120323
AB1135001989 | 20120423
.
.
.
I want to pair the data in date-ranges based on DATE.
AB1135000097 | 20151221 | 20160119
AB1135000097 | 20160119 | 20160219
AB1135001989 | 20120223 | 20120323
AB1135001989 | 20120323 | 20120423
Does this action have a name ? (I will add tags to the post when I know what I'm asking for)
Assumed schema
I am assuming that your table is like:
CREATE TABLE "TABLE"
(
tag CHAR(1) NOT NULL,
value INTEGER NOT NULL,
PRIMARY KEY(tag, value)
);
I really shouldn't have to guess the schema though.
Possible answer
Superficially, you might be after:
SELECT t1.tag, t1.value, t2.value
FROM "TABLE" AS t1
JOIN "TABLE" AS t2
ON t1.tag = t2.tag AND t2.value = t1.value + 1
ORDER BY t1.tag, t1.value;
This joins the table with itself, combining rows where the tag column values (A, B, ...) are the same, and where the value column in one row is one more than the value column in the other.
On the other hand, if you add a row ('A', 5) to the table and expect it to appear in the output as part of a row ('A', 3, 5), then the query is much harder to write without using OLAP features.
if you are using Oracle database then you can refer following query to solve this question -
with t as
(
SELECT 'A' Col1, 1 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 2 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 3 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 4 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 5 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 6 Col2 FROM Dual
)
SELECT *
FROM (SELECT Col1,
Col2,
Lead(Col1) Over(ORDER BY Col1, Col2) Col3,
Lead(Col2) Over(ORDER BY Col1, Col2) Col4
FROM t --(your table name)
ORDER BY Col1, Col2)
WHERE Col1 = Col3
as I don't have your table name and table structure I have created one temp table in Query itself.
you need to change From t to From with your table name . .. please change col1 and col2 column name also accordingly.
I found a solution to my problem. Inspired by Jonathan Leffler's solution. Thanks a lot!
It is based on adding row-numbers to the table ordered by ID and DATE, and then self-join with ROW+1 to get the next date as a second date column.
with
SCHEDULE as
( -- remove duplicates and NULL entries
select DISTINCT ID, DATE from TABLE1
where DATE IS NOT NULL
),
SCHEDULE_WITH_ROW as
(
select * from (
select DISTINCT ROW_NUMBER()
OVER (ORDER BY ID, DATE) AS
ROW, ID, DATE
from SCHEDULE) AS SCHED
)
select
S1.ID
, S1.DATE
, S2.DATE
from SCHEDULE_WITH_ROW S1
join SCHEDULE_WITH_ROW S2 on S2.ID = S1.ID and S1.ROW + 1 = S2.ROW

SQL query to select records from three Tables seperatly

SQL query which select the record from three tables and there is no relation between these tables. Actually I want to make it a VIEW.
suppose there are three tales Table1, Table2, Table3
I want to show records of Table1 first with some filter criteria
and then the records from Table2
and in last from Table3 as when we execute the view it show like the records like a Table.
There can be any number of rows but the records must be in this sequence.
I would suggest using UNION ALL instead of union if you want all the records from each of the tables. UNION will use a distinct to filter out duplicates. If you don't need tht it is just slowing down the query.
A further explanation here:
http://wiki.lessthandot.com/index.php/Union_All
To show you how to handle when you don't have all the columns in each table:
select
1 as seq,col1, col2, col3, cast(null as varchar (40)) as col4
FROM Table1
where ...
UNION ALL
select
2 as seq,'Unknown', col2, null, col4
FROM Table2
where ...
UNION ALL
select
3 as seq ,col1, col2, col3, cast(null as varchar (40)) as col4
FROM Table3
where ...
ORDER BY seq
try:
select
1,col1, col2, col3
FROM Table1
where ...
UNION ALL
select
2,col1, col2, col3
FROM Table2
where ...
UNION ALL
select
3,col1, col2, col3
FROM Table3
where ...
ORDER BY 1
please note that each of the three queries needs to have the same number of columns and that the data types should be consistent also. Also, I used UNION ALL to speed up the query, since there is no use eliminating duplicates between the three queries because the sequence table will guarantee no dups.
to not have the sequence column in the result set try:
SELECT
col1,col2,col3
FROM (select
1 as seq,col1, col2, col3
FROM Table1
where ...
UNION ALL
select
2 as seq,col1, col2, col3
FROM Table2
where ...
UNION ALL
select
3 as seq,col1, col2, col3
FROM Table3
where ...
) dt
ORDER BY seq
you can use a UNION query:
SELECT Field1, Field2, Field3, '1' as Sequence FROM Table1 WHERE SomeCriteria
UNION
SELECT Field7, Field5, Field6, '2' FROM Table2 WHERE SomeCriteria
UNION
SELECT Field4, Field8, Field9, '3' FROM Table3 WHERE SomeCriteria
How about:
create view AZ_VIEW as
select 1 as orderby, tbl1Col1 as col1, tbl1Col2 as col2, tbl1col3 as col3 from Table1 where criteria1='val'
union
select 2, tbl2Col1, tbl2Col2, tbl2col3 from Table2 where criteria2='anotherval'
union
select 3, tbl3Col1, tbl3Col2, tbl3col3 from Table3 where criteria3='athirdval'
;
If your tables share the same columns, you can use Union All:
Select col1, col2, 1 As seq
From table1
Union All
Select col1, col2, 2 As seq
From table1
Union All
Select col1, col2, 3 As seq
From table1
Order By seq
You can UNION the three tables, taking care to ensure that they all return the same number of fields. There is a simple cheat to control the order (seen below):
SELECT * FROM
(
SELECT a, b, c, 1 as ListOrder FROM table1
UNION
SELECT a, b, c, 2 as ListOrder FROM table2
UNION
SELECT a, b, c, 3 as ListOrder FROM table3
)
ORDER BY ListOrder
You can do something like this - WHERE ID = 34 is just a sample filter:
create view vAllRecords as
select 1 as Rank, Field1, Field2 from Table1 where ID = 34
UNION
select 2 as Rank, Field1, Field2 from Table2
UNION
select 3 as Rank, Field1, Field2 from Table3
The use of UNION will remove any duplicates. If you know there will be no duplicates, or you want to see them, the query will run faster with UNION ALL instead.
ORDER BY is not allowed in views, so you'll need to order by Rank when you select from the view:
select *
from vAllRecords
order by Rank