How to do a join in Oracle tables? - sql

I am trying to help a co-worker do an inner join on two oracle tables so he can build a particular graph on a report.
I have no Oracle experience, only SQL Server and have gotten to what seems like the appropriate statement, but does not work.
SELECT concat(concat(month("a.timestamp"),','),day("a.timestamp")) as monthDay
, min("a.data_value") as minTemp
, max("a.data_value") as maxTemp
, "b.forecast" as forecastTemp
, "a.timestamp" as date
FROM table1 a
WHERE "a.category" = 'temperature'
GROUP BY concat(concat(month("timestamp"),','),day("timestamp"))
INNER JOIN (SELECT "forecast"
, "timestamp"
FROM table2
WHERE "category" = 'temperature') b
ON "a.timestamp" = "b.timestamp"
It doesn't like my aliases for some reason. It doesn't like not having quotes for some reason.
Also when I use the fully scored names it still fails because:
ORA-00933 SQL command not properly ended

The order of the query should be
SELECT
FROM
INNER JOIN
WHERE
GROUP BY
as below
SELECT concat(concat(month("a.timestamp"),','),day("a.timestamp")) as monthDay
, min("a.data_value") as minTemp
, max("a.data_value") as maxTemp
, "b.forecast" as forecastTemp
, "a.timestamp" as date
FROM table1 a
INNER JOIN (SELECT "forecast"
, "timestamp"
FROM table2
WHERE "category" = 'temperature') b
ON "a.timestamp" = "b.timestamp"
WHERE "category" = 'temperature'
GROUP BY concat(concat(month("timestamp"),','),day("timestamp"))

In a flood of attempts, here's yet another one.
table2 can be moved out of subquery; join it with table1 on category as well
note that all non-aggregates columns (from the SELECT) have to be contained in the GROUP BY clause. It seems that a.timestamp contains more info than just month and day - if that's so, it'll probably ruin the whole result set as data won't be grouped by monthday, but by the whole date - consider removing it from SELECT, if necessary
SELECT TO_CHAR(a.timestamp,'mm.dd') monthday,
MIN(a.data_value) mintemp,
MAX(a.data_value) maxtemp,
b.forecast forecasttemp,
a.timestamp c_date
FROM table1 a
JOIN table2 b ON a.timestamp = b.timestamp
AND a.category = b.category
WHERE a.category = 'temperature'
GROUP BY TO_CHAR(a.timestamp,'mm.dd'),
b.forecast,
a.timestamp;

The correct (simplified) syntax of select is
SELECT <columns>
FROM table1 <alias>
JOIN table2 <alias> <join_condition>
WHERE <condition>
GROUP BY <group by columns>

You are doing it wrong. Use subquery:
SELECT c.*, b.`forecast` as forecastTemp
FROM
(SELECT concat(concat(month(a.`timestamp`),','),day(a.`timestamp`)) as monthDay
, min(a.`data_value`) as minTemp
, max(a.`data_value`) as maxTemp
, a.`timestamp` as date
FROM table1 a
WHERE `category`='temperature'
GROUP BY concat(concat(month(`timestamp`),','),day(`timestamp`))) c
INNER JOIN (SELECT `forecast`
, `timestamp`
FROM table2
WHERE `category` = 'temperature') b
ON c.`timestamp` = b.`timestamp`;

In addition to the order of the components other answers have mentioned (where goes after join etc), you also need to remove all of the double-quote characters. In Oracle, these override the standard naming rules, so "a.category" is only valid if your table actually has a column named, literally, "a.category", e.g.
create table demo ("a.category" varchar2(10));
insert into demo ("a.category") values ('Weird');
select d."a.category" from demo d;
It's quite rare to need to do this.

The query should look something like this:
SELECT to_char(a.timestamp, 'MM-DD') as monthDay,
min(a.data_value) as minTemp,
max(a.data_value) as maxTemp,
b.forecast as forecastTemp
FROM table1 a JOIN
table2 b
ON a.timestamp = b.timestamp and b.category = 'temperature'
WHERE a.category = 'temperature'
GROUP BY to_char(timestamp, 'MM-DD'), b.forecast;
I'm not 100% sure this is what you want. Your query has numerous issues and complexities:
You don't need a subquery in the FROM clause.
You can use to_char() instead of the more complex date string processing.
The group by did not contain all the relevant fields.
Don't use double quotes, unless really, really needed.

Related

(probably) very simple SQL query needed

Having a slow day....could use some assistance writing a simple ANSI SQL query.
I have a list of individuals within families (first and last names), and a second table which lists a subset of those individuals. I would like to create a third table which flags every individual within a family if ANY of the individuals are not listed in the second table. The goal is essentially to flag "incomplete" families.
Below is an example of the two input tables, and the desired third table.
As I said...very simple...having a slow day. Thanks!
I think you want a left join and case expression:
select t1.*,
(case when t2.first_name is null then 'INCOMPLETE' else 'OK' end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
Of course, this marks "Diane Thomson" as "OK", but I think that is an error in the question.
EDIT:
Oh, I see. The last name defines the family (that seems like a pretty big assumption). But you can do this with window functions:
select t1.*,
(case when count(t2.first_name) over (partition by t1.last_name) =
count(*) over (partition by t1.last_name)
then 'OK'
else 'INCOMPLETE'
end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
That's not simple, at least not in SAS :-)
Standard SQL, when Windowed Aggregates are supported:
select ft.*,
-- counts differ when st.first_name is null due to the outer join
case when count(*) over (partition by ft.last_name)
= count(st.first_name) over (partition by ft.last_name)
then 'OK'
else 'INCOMPLETE'
end
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = ft.last_name
Otherwise you need to a standard aggregate and join back:
select ft.*, st.flag
from first_table as ft
join
(
select ft.last_name,
case when count(*)
= count(st.first_name)
then 'OK'
else 'INCOMPLETE'
end as flag
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = st.last_name
group by ft.last_name
) as st
on ft.last_name = st.last_name
It is pretty easy to do in SAS if you want to take advantage of its non-ANSI SQL feature of automatically re-merging aggregate function results back onto detail records.
select
a.first
, a.last
, case when 1=max(missing(b.last)) then 'INCOMPLETE'
else 'OK'
end as flag
from table1 a left join table2 b
on a.last=b.last and a.first=b.first
group by 2
order by 2,1
;

How to join 100 random rows from table 1 multiple other tables in oracle

I have scrapped my previous question as I did not do a good job explaining. Maybe this will be simpler.
I have the following query.
Select * from comp_eval_hdr, comp_eval_pi_xref, core_pi, comp_eval_dtl
where comp_eval_hdr.START_DATE between TO_DATE('01-JAN-16' , 'DD-MON-YY')
and TO_DATE('12-DEC-17' , 'DD-MON-YY')
and comp_eval_hdr.COMP_EVAL_ID = comp_eval_dtl.COMP_EVAL_ID
and comp_eval_hdr.COMP_EVAL_ID = comp_eval_pi_xref.COMP_EVAL_ID
and core_pi.PI_ID = comp_eval_pi_xref.PI_ID
and core_pi.PROGRAM_CODE = 'PS'
Now if I only want a random 100 rows from the comp_eval_hdr table to join with the other tables how would I go about it? If it makes it easier you can disregard the comp_eval_dtl table.
I think you are pretty much there. You just need subqueries, table aliases, and JOIN conditions:
SELECT . . .
FROM (SELECT a.*
FROM (SELECT a.*
FROM a
WHERE a.START_DATE BEWTWEEN DATE '2016-01-01' AND DATE '2017-12-12'
ORDER BY DBMS_RANDOM.VALUE
) a
WHERE ROWNUM <= 100
) a JOIN
mapping m
ON a.? = m.? JOIN
b
ON m.? = b.?;
The ? is just a placeholder for the join columns.
It's a bit of a stretch to know what you want with the question as written but here's my attempt.
WITH rand_list AS
(SELECT * FROM comp_eval_hdr
WHERE comp_eval_hdr.START_DATE BEWTWEEN TO_DATE('01-JAN-16' , 'DD-MON-YY') AND TO_DATE('12-DEC-17' , 'DD-MON-YY')
ORDER BY DBMS_RANDOM.VALUE)
first_100 AS
(SELECT *
FROM rand_list
WHERE ROWNUM <=100)
SELECT md.col_1, t3.col_a
FROM first_100 md
INNER JOIN
table2 t2 ON md.id_column = t2.fk_comp_eval_hdr_id
INNER JOIN
table3 t3 ON t3.id_column = t2.fk_table3_id
You haven't given any indication how they join or the table names and obviously I haven't run this against any mock tables.
You've got a list of randomised records with RAND_LIST which you could, if you wanted, combine with the FIRST_100 query (your choice).
The main query then just joins that through your mapping table (T2) to your 'multiples' table (T3).
how does table 2 look like?...Let me put one example as person table and order table?
select * from (
select * from person ps , order order where ps.city = 'mumbai' and ps.id = order.purchasedby ) porder where porder.rownum <= 100
I did not tested it but it will look something like this.

SQL Inner Join and nearest row to date

I dont't get it. I changed some of the code. In the WPLEVENT Table are a lot of Events per person. In the Persab-Table are the Persons with their History. Now I need the from the Persab Table just that row wich matches the persab.gltab Date nearest to the WPLEVENT.vdat Date. So all rows from the WPLEVENT, but just the one matching row from the PERSAB-Table.
SELECT
persab.name,
persab.vorname,
vdat,
eventstart,
persab.rc1,
persab.rc2
FROM wplevent
INNER JOIN
persab ON WPLEVENT.PersID = persab.PRIMKEY
INNER JOIN
(SELECT TOP 1 persab.rc1
FROM PERSAB
WHERE persab.gltab <= getdate() --/ Should be wplevent.vdat instead of getdate()
) NewTable ON wplevent.persid = persab.primkey
WHERE
persid ='100458'
ORDER BY vdat DESC
Need to use the MAX() function with the proper syntax by supplying an expression like MAX(persab.rc1). Also need to use GROUP BY for the second column rc2 in the subquery (although it looks like you do not need it). Finally you are missing the ON clause for the final INNER JOIN. I can update the answer to fix the query if you provide that information.
SELECT
Z1PERS.NAME
, Z1PERS.VORNAME
, WPLEVENT.VDat
, WPLEVENT.EventStart
, WPLEVENT.EventStop
, WPLEVENT.PEPGROUP
, Z1SGRP.TXXT
, PERSAB.GLTAB
, Z1PERS.PRIMKEY AS Expr1
, PERSAB.PRIMKEY
FROM
Z1PERS
INNER JOIN
WPLEVENT ON Z1PERS.PRIMKEY = WPLEVENT.PersID
INNER JOIN
Z1SGRP ON WPLEVENT.PEPGROUP = Z1SGRP.GRUPPE
INNER JOIN
(
SELECT MAX(Persab.rc1) --Fixed MAX expression
, persab.rc2
FROM
persab
GROUP BY
persab.rc2 --Need to group on rc2 if you want that column in the query otherwise remove this AND the rc2 column from select list
WHERE
WPLEVENT.PersID = PERSAB.PRIMKEY
AND WPLEVENT.VDat <= PERSAB.GLTAB
) --Missing ON clause for the INNER JOIN here
WHERE z1pers.vorname = 'henning'

Why do I get ORA-00907 in my SQL query?

I have this SQL query which a partner has done for a little project at university (this is the first time we use SQL), but we get the ora-00907 error and both of us don't know why.
I have checked the parenthesis and they seem to be ok, so the problem must be another.
select
persona.nombre,
anyo,
t2.total
from persona join
(
select
t1.idPersona,
count(produccion.anyo) as total,
anyo
from
(
select *
from produccion
join pelicula
on produccion.id = pelicula.id
) as pel
join
(
select *
from participa
where idPapel = 8
) as t1
on t1.idProduccion = pel.id
)
group by t1.idPersona
) as t2
on persona.id = t2.idPersona
where t2.total > 2
order by t2.total desc;
You are selecting * and doing group by on one column which is creating problem. Either you select only respective column under group by condition OR you remove group by.
select *
from (produccion join pelicula on produccion.id=pelicula.id) as pel
join
(select *
from participa
where idPapel=8) as t1
on t1.idProduccion=pel.id)
group by t1.idPersona
Above code section is unallowed use of group by.
If group by is so much needed, i would suggest you to use it later on in the end. Another option is to use analytical function and filter out rest un-wanted records in upper nesting of query which you already have.
You have lots of nested views, which makes your query rather hard to debug. You have lots of brackets, which need to match.
Anyway this is wrong: select t1.idPersona, count(produccion.anyo) as total, anyo. You'll need to include anyo in the GROUP BY clause, which will probably change the result set you want.
select persona.nombre,
t2.anyo,
t2.total
from persona join
(select t1.idPersona,
count(produccion.anyo) as total,
anyo
from (select *
from produccion
join pelicula
on produccion.id=pelicula.id) pel
join
(select *
from participa
where idPapel=8) t1
on t1.idProduccion=pel.id
group by t1.idPersona, t1.anyo) t2
on persona.id=t2.idPersona
where t2.total>2
order by t2.total desc;
I think your query can be simplified/corrected like this:
select persona.nombre,
anyo,
t2.total
from persona
join (
select par.idPersona,
count(produccion.anyo) as total,
anyo
from produccion
join pelicula
on produccion.id = pelicula.id
left join participa par
on par.idProduccion = pelicula.id -- or produccion.id,
-- this was also an error in the original query,
-- since the subquery selected both
and par.idPapel = 8
group by t1.idPersona
, anyo -- Was missing, but it also doesn't make sense, as this is what you count, so you'll just get 1's here. What do you want with this?
) as t2
on persona.id = t2.idPersona
where t2.total > 2
order by t2.total desc;

How to convert SUBSELECT with TOP and ORDER BY to JOIN

I have a working sql select, which looks like this
[Edited: Im sorry i did one mistake in the question, i edited alias of Table1 but im trying the answers]
SELECT
m.Column1
,t2.Column2
,COALESCE
(
(
SELECT TOP 1 Vat
FROM LinkedDBServer.DatabaseName.dbo.TableName t3
WHERE
m.MaterialNumber = t3.MaterialNumber COLLATE Czech_CI_AS
and t3.Currency = …
and ...
ORDER BY [Date] DESC
), m.Vat
) as Vat
FROM Table1 m
JOIN Table2 t2 on (m.Column1 = t2.Column1)
It works but the problem is that it takes too long and LinkedServer cut my connection because it takes more than 10 minutes. The purpose of the query is to get newer data from a different database if it exists (i get newest data by top and ordering it by date and precondition is that every data in that database is newer than in mine, thats why im using COALESCE).
But my though is if I was able to rewrite it to JOIN it could be faster. But another problem could be I dont have an primary key (and cant change that).
How can I speed that query up ? (Im using SQL Server 2008 R2)
Thank you
Here i attached Estimated Query Plan: (Its readable in browser ZOOM :) Estimation is for 2 Coalesce columns.
Try rewriting query using outer apply
SELECT
t1.Column1
,t2.Column2
,COALESCE(ou.vat, m.Vat) as Vat
FROM Table1 t1
JOIN Table2 m on (m.Column1 = t1.Column1)
outer apply
(
SELECT TOP 1 Vat
FROM LinkedDBServer.DatabaseName.dbo.TableName t3
WHERE
m.MaterialNumber = t3.MaterialNumber COLLATE Czech_CI_AS
and t3.Currency = …
and ...
ORDER BY [Date] DESC
) ou
Another option:
; WITH vat AS (
SELECT MaterialNumber COLLATE Czech_CI_AS As MaterialNumber
, Vat
, Row_Number() OVER (PARTITION BY MaterialNumber ORDER BY "Date" DESC) As sequence
FROM LinkedDBServer.DatabaseName.dbo.TableName
WHERE Currency = ...
AND ...
)
SELECT t1.Column1
, m.Column2
, Coalesce(vat.Vat, m.Vat) As Vat
FROM Table1 As t1
INNER
JOIN Table2 As m
ON m.Column1 = t1.Column1
LEFT
JOIN vat
ON vat.MaterialNumber = m.MaterialNumber
AND vat.sequence = 1
;