I am joining two tables with bad performances.
Table1 :
Period
Zone
Country
company
product
01/01/2020
EMEA
DE
WKDM2
Product1
01/02/2020
EMEA
DE
PRL56
Product1
01/03/2020
EMEA
UK
ORD56
Product2
01/04/2020
EMEA
DE
GFDS
Product3
01/05/2020
EMEA
FR
24GFDSGF2
Product1
01/06/2020
EMEA
DE
2GFSDG37
Product3
01/07/2020
EMEA
IT
2GFDSG35
Product1
01/08/2020
EMEA
DE
23GSFDG6
Product4
01/09/2020
EMEA
DE
23GSFDG5
Product6
01/10/2020
EMEA
IT
24GSFD1
Product1
01/11/2020
EMEA
DE
23GSDF6
Product3
01/12/2020
EMEA
FI
24GFSDG1
Product8
Table2:
Period
Zone
Country
Quarter
Year
company
product
01/01/2020
EMEA
DE
01/01/2020
01/01/2020
WKDM2
Product1
01/02/2020
EMEA
DE
01/01/2020
01/01/2020
PRL56
Product1
01/03/2020
EMEA
UK
01/01/2020
01/01/2020
ORD56
Product2
01/04/2020
EMEA
DE
01/04/2020
01/01/2020
GFDS
Product3
01/05/2020
EMEA
FR
01/04/2020
01/01/2020
24GFDSGF2
Product1
01/06/2020
EMEA
DE
01/04/2020
01/01/2020
2GFSDG37
Product3
01/07/2020
EMEA
IT
01/07/2020
01/01/2020
2GFDSG35
Product1
01/08/2020
EMEA
DE
01/07/2020
01/01/2020
23GSFDG6
Product4
01/09/2020
EMEA
DE
01/07/2020
01/01/2020
23GSFDG5
Product6
01/10/2020
EMEA
IT
01/10/2020
01/01/2020
24GSFD1
Product1
01/11/2020
EMEA
DE
01/10/2020
01/01/2020
23GSDF6
Product3
01/12/2020
EMEA
FI
01/10/2020
01/01/2020
24GFSDG1
Product8
In my exemple, the data is the same but in Production ENV, Company on table 1 is the source of truth. And product on 2nd table is the source of truth.
Table1 have 6M rows and table2 600K rows.
When i join like this i have poor performances, how can i improve this ?:
SELECT R."Period",R."Zone",R."Country",S."Quarter",S."Year",R."Company",S."Product"
FROM table1 AS R,
table2 AS S
WHERE R."Period" = S."Period"
AND R."Zone" = S."Zone"
AND R."Country"=S."Country"
GROUP BY 1,2,3,4,5,6,7
UPDATE:
TEST DATASET :
CREATE OR REPLACE TEMPORARY TABLE "TMP_TEST1" (
"Period" TIMESTAMP,
"Country" VARCHAR,
"Quarter" TIMESTAMP,
"Year" TIMESTAMP,
"Company" VARCHAR,
"Product" VARCHAR
);
INSERT INTO "TMP_TEST1"
VALUES
('01/01/2020','DE','01/01/2020 ','01/01/2020 ','WKDM2 ','Product1'),
('01/01/2020','DE','01/01/2020 ','01/01/2020 ','2GFSDG37 ','Product1'),
('01/02/2020','DE','01/01/2020 ','01/01/2020 ','ORD56 ','Product2'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','GFDS ','Product3'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','24GFDSGF2 ','Product1'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','24GSFD1 ','Product1'),
('01/04/2020','DE','01/04/2020 ','01/01/2020 ','2GFSDG37 ','Product4'),
('01/04/2020','DE','01/04/2020 ','01/01/2020 ','23GSFDG5 ','Product6'),
('01/05/2020','DE','01/04/2020 ','01/01/2020 ','23GSDF6 ','Product3'),
('01/06/2020','DE','01/04/2020 ','01/01/2020 ','24GSFD1 ','Product8');
CREATE OR REPLACE TEMPORARY TABLE "TMP_TEST2" (
"Period" TIMESTAMP,
"Country" VARCHAR,
"Quarter" TIMESTAMP,
"Year" TIMESTAMP,
"Company" VARCHAR,
"Product" VARCHAR
);
INSERT INTO "TMP_TEST2"
VALUES
('01/01/2020','DE','01/01/2020 ','01/01/2020 ','WKDM2 ','Product1'),
('01/01/2020','DE','01/01/2020 ','01/01/2020 ','2GFSDG37 ','Product1'),
('01/02/2020','DE','01/01/2020 ','01/01/2020 ','ORD56 ','Product2'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','GFDS ','Product3'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','24GFDSGF2 ','Product1'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','2GFSDG37 ','Product3'),
('01/03/2020','DE','01/01/2020 ','01/01/2020 ','24GSFD1 ','Product1'),
('01/04/2020','DE','01/04/2020 ','01/01/2020 ','2GFSDG37 ','Product4'),
('01/04/2020','DE','01/04/2020 ','01/01/2020 ','23GSFDG5 ','Product6'),
('01/04/2020','DE','01/04/2020 ','01/01/2020 ','24GSFD1 ','Product1'),
('01/05/2020','DE','01/04/2020 ','01/01/2020 ','23GSDF6 ','Product3'),
('01/05/2020','DE','01/04/2020 ','01/01/2020 ','23GSDF6 ','Product9'),
('01/06/2020','DE','01/04/2020 ','01/01/2020 ','24GSFD1 ','Product8');
QUERY:
SELECT DISTINCT T1."Period",
T1."Country",
T1."Quarter",
T1."Year",
T1."Company",
T2."Product"
FROM TMP_TEST1 AS T1 INNER JOIN TMP_TEST2 AS T2
ON T1."Period" = T2."Period"
AND T1."Country"=T2."Country"
GROUP BY 1,2,3,4,5,6
With this test dataset you will see that i loose Products when there is no company. I don't know how to break this relation. I hope i am clear enough for you.
Firstly, given the data in the 2 source tables you have shown, please also provide the result you want to see.
For the query you have given, the correct way to write it, using ANSI SQL, is as follows:
SELECT R."Period",R."Zone",R."Country",S."Quarter",S."Year",R."Company",S."Product"
FROM table1 AS R
INNER JOIN table2 AS S ON
R."Period" = S."Period"
AND R."Zone" = S."Zone"
AND R."Country"=S."Country"
GROUP BY 1,2,3,4,5,6,7
To repeat my questions (and add some more)
How long is the query currently taking?
How long, roughly, should it take for you to consider the performance to be acceptable?
Why are you using GROUP BY when you have no aggregate functions in your query? If you want a distinct list (and your query is definitely producing duplicates) then use SELECT DISTINCT...
What do you mean by "cartesian calculations"? Do you mean cartesian joins and, if you do, why have you mentioned them as you don't have a cartesian join?
Response to Comments
ANSI SQL joins are much easier to read (and debug) as all the join information is in the JOIN statements and all the filtering conditions are in the WHERE statements - plus it is the industry standard so it would be a good idea for you, as a beginner, to get used to using it now rather than learning bad practices. Imagine if you were joining 20 tables with a mixture of inner/outer/left/right joins - the syntax you are using would be pretty incomprehensible whereas ANSI SQL join syntax would be simple to understand.
You still haven't provided the output that you are expecting to see, based on your source tables - so anyone trying to help you is left guessing what it is you are trying to achieve.
You also haven't provided the Explain Plan so no-one can see how your query is executing and therefore what the problem might be. In Snowflake, go to History, click on the relevant Query ID, click on Profile and then attach a screenshot showing all the steps being run, the execution time, the statistics, etc.
So I started making some tables that have 6M rows of data and 600K to show how joins via text is bad and use id's
CREATE TABLE table1 AS
with periods AS (
SELECT dateadd('day', SEQ8(), '1999-01-01'::date) as period
FROM TABLE(GENERATOR(rowcount=>1000))
), zones AS (
SELECT column1 as zone
,seq8() as zone_id
FROM VALUES ('EMEA')
), countries AS (
SELECT column1 as country
,seq8() as country_id
FROM VALUES ('DE'),('UK'),('FR'),('IT'),('NZ'),('AU'),('US'),('CA'),('xx'),('yy')
), company AS (
SELECT seq8() as comp_id
,hash(comp_id) as h_comp_id
,h_comp_id::text as t_h_comp_id
FROM TABLE(GENERATOR(rowcount=>600))
)
SELECT p.*, z.*, c.*, co.*
FROM periods p
JOIN zones z ON true
JOIN countries c ON true
JOIN company co ON true
;
CREATE TABLE table2 AS
SELECT *, YEAR(period) as year, QUARTER(period) as quarter
FROM table1
LIMIT 600000;
and then ran the SQL
SELECT R.Period,R.Zone,R.Country,S.Quarter,S.Year,R.comp_id
FROM table1 AS R
JOIN table2 AS S
ON R.Period = S.Period
AND R.Zone = S.Zone
AND R.Country=S.Country;
-- 1m27s
And it took ages, so ran my "fast" SQL
SELECT R.Period,R.Zone,R.Country,S.Quarter,S.Year,R.comp_id
FROM table1 AS R
JOIN table2 AS S
ON R.Period = S.Period
AND R.Zone_id = S.Zone_id
AND R.Country_id=S.Country_id;
-- 1m18s
and it took ages also.
Looking at the profile 90% of the time was getting the results.
SELECT S.Year, count(*) as c
FROM table1 AS R
JOIN table2 AS S
ON R.Period = S.Period
AND R.Zone = S.Zone
AND R.Country=S.Country
GROUP BY 1;
-- 9s
swapping to an aggregate to avoid the fetch, bad joins are 9 seconds
SELECT S.Year, count(*) as c
FROM table1 AS R
JOIN table2 AS S
ON R.Period = S.Period
AND R.Zone_id = S.Zone_id
AND R.Country_id=S.Country_id
GROUP BY 1;
-- 6s
and good joins are 6 seconds. So text is "still bad" but really fetching 3.6 millions rows of text is really slow.
Related
I have 2 tables where I want to join it and gets its data. For ex: Table A consists of Error Code and the description of its code is in Table B.
So the value of Error Code is stored like ,1, ,2, ,1,2 in table A.
tbl_fiber_invalid_trans_data -> Table A
TBL_INVALID_ERROR_DATA -> Table B.
I tried joining like below but the Remarks was all blank
SELECT a.SPAN_ID,MAINTENANCE_ZONE_NAME,a.MAINTENANCE_ZONE_CODE,a.R4G_STATE_NAME,
a.NETWORK_CATEGORY,a.NETWORK_TYPE,a.CONSTRUCTION_METHODOLOGY,
a.INVENTORY_STATUS_CODE,a.OWNERSHIP_TYPE_CODE,a.ROUTE_NAME,a.INTRACITY_LINK_ID ,
a.CALCULATED_LENGTH, REPLACE(a.REMARKS, ',1', '1') as REMARKS
FROM tbl_fiber_invalid_trans_data a
left JOIN TBL_INVALID_ERROR_DATA t
ON a.REMARKS = t.ID;
Let me know where I am wrong. and how to replace all those comma separated values
update
Table:tbl_fiber_invalid_trans_data
Name Null Type
------------------------ ---- ---------------
SPAN_ID NVARCHAR2(100)
MAINTENANCE_ZONE_NAME NVARCHAR2(100)
MAINTENANCE_ZONE_CODE NVARCHAR2(50)
R4G_STATE_NAME NVARCHAR2(50)
STATE_NAME NVARCHAR2(50)
NETWORK_CATEGORY NVARCHAR2(100)
NETWORK_TYPE NVARCHAR2(100)
CONSTRUCTION_METHODOLOGY NVARCHAR2(50)
INVENTORY_STATUS_CODE NVARCHAR2(20)
OWNERSHIP_TYPE_CODE NVARCHAR2(20)
ROUTE_NAME NVARCHAR2(100)
INTRACITY_LINK_ID NVARCHAR2(100)
CALCULATED_LENGTH NUMBER(38,8)
LAST_UPDATED_BY NVARCHAR2(100)
LAST_UPDATED_DATE DATE
REMARKS NVARCHAR2(1000)
Table:TBL_INVALID_ERROR_DATA
Name Null Type
-------- ---- --------------
ID NUMBER(18,8)
ERR_CODE NVARCHAR2(500)
Sample data as followed
Table 1
[![img1][1]][1]
Sample data table 2
[![enter image description here][2]][2]
Here's a simplified example that shows how you can extract integers from a comma separated list (REMARKS in tbl_fiber_invalid_trans_data), and then join that to the error codes list (TBL_INVALID_ERROR_DATA) to get the messages:
WITH codes AS (
SELECT DISTINCT SPAN_ID, REGEXP_SUBSTR(remarks, '\d+', 1, level) AS code
FROM tbl_fiber_invalid_trans_data
CONNECT BY REGEXP_SUBSTR(remarks, '\d+', 1, level) IS NOT NULL
)
SELECT t1.*, t2.err_code
FROM tbl_fiber_invalid_trans_data t1
JOIN codes c ON c.SPAN_ID = t1.SPAN_ID
LEFT JOIN TBL_INVALID_ERROR_DATA t2 ON t2.id = c.code
ORDER BY t1.SPAN_ID
Output (for my simplified demo):
SPAN_ID MAINTENANCE_ZONE_NAME REMARKS ERR_CODE
1 Zone 1 ,1 Span id length too short
2 Zone 2 ,2 Inventory suspended
3 Zone 3 ,1,2 Span id length too short
3 Zone 3 ,1,2 Inventory suspended
4 Zone 4 ,2,1 Span id length too short
4 Zone 4 ,2,1 Inventory suspended
5 Zone 5 null null
If you want all the errors for a zone in one line, you can aggregate them in a second CTE and JOIN to that instead:
WITH codes AS (
SELECT DISTINCT SPAN_ID, REGEXP_SUBSTR(remarks, '\d+', 1, level) AS code
FROM tbl_fiber_invalid_trans_data
CONNECT BY REGEXP_SUBSTR(remarks, '\d+', 1, level) IS NOT NULL
),
msgs AS (
SELECT SPAN_ID, LISTAGG(err_code, ', ') WITHIN GROUP (ORDER BY code) AS err_codes
FROM codes c
LEFT JOIN TBL_INVALID_ERROR_DATA t2 ON t2.id = c.code
GROUP BY SPAN_ID
)
SELECT t1.*, m.err_codes
FROM tbl_fiber_invalid_trans_data t1
JOIN msgs m ON m.SPAN_ID = t1.SPAN_ID
ORDER BY t1.SPAN_ID
Output
SPAN_ID MAINTENANCE_ZONE_NAME REMARKS ERR_CODES
1 Zone 1 ,1 Span id length too short
2 Zone 2 ,2 Inventory suspended
3 Zone 3 ,1,2 Span id length too short, Inventory suspended
4 Zone 4 ,2,1 Span id length too short, Inventory suspended
5 Zone 5 null null
Demo on dbfiddle
You need a join with LIKE as follows:
ON a.REMARKS || ',' like '%,'|| t.ID || ',%';
Update
You must use the following query:
SELECT SPAN_ID,
MAINTENANCE_ZONE_NAME,
MAINTENANCE_ZONE_CODE,
R4G_STATE_NAME,
NETWORK_CATEGORY,
NETWORK_TYPE,
CONSTRUCTION_METHODOLOGY,
INVENTORY_STATUS_CODE,
OWNERSHIP_TYPE_CODE,
ROUTE_NAME,
INTRACITY_LINK_ID,
CALCULATED_LENGTH,
LISTAGG(ID,',') WITHIN GROUP (ORDER BY ID) AS REMARKS,
LISTAGG(ERR_CODE,',') WITHIN GROUP (ORDER BY ID) AS ERR_CODE
FROM
(SELECT DISTINCT A.SPAN_ID,
MAINTENANCE_ZONE_NAME,
A.MAINTENANCE_ZONE_CODE,
A.R4G_STATE_NAME,
A.NETWORK_CATEGORY,
A.NETWORK_TYPE,
A.CONSTRUCTION_METHODOLOGY,
A.INVENTORY_STATUS_CODE,
A.OWNERSHIP_TYPE_CODE,
A.ROUTE_NAME,
A.INTRACITY_LINK_ID,
A.CALCULATED_LENGTH,
T.ID,
T.ERR_CODE
FROM TBL_FIBER_INVALID_TRANS_DATA A
LEFT JOIN TBL_INVALID_ERROR_DATA T
ON A.REMARKS || ',' LIKE '%,' || T.ID || ',%')
GROUP BY SPAN_ID,
MAINTENANCE_ZONE_NAME,
MAINTENANCE_ZONE_CODE,
R4G_STATE_NAME,
NETWORK_CATEGORY,
NETWORK_TYPE,
CONSTRUCTION_METHODOLOGY,
INVENTORY_STATUS_CODE,
OWNERSHIP_TYPE_CODE,
ROUTE_NAME,
INTRACITY_LINK_ID,
CALCULATED_LENGTH
I have the following oracle 12c tables structure:
table1:
path_id node_1 node1_port node_2 node2_port
------------------------------------------------------------------------
1 France France_port1 Italy Italy_port1
1 Italy Italy_port1 France France_port1
1 Italy Italy_port2 Belgium Belgium_port1
1 Belgium Belgium_port1 Italy Italy_port2
1 Belgium Belgium_port2 Sweden Sweden_port1
1 Sweden Sweden_port1 Belgium Belgium_port2
2 Belgium Belgium_port1 Germany Germany_port1
2 Germany Germany_port1 Belgium Belgium_port1
table2:
path_id start_node start_node_port end_node end_node_port
----------------------------------------------------------------
1 France France_port1 Sweden Sweden_port1
2 Belgium Belgium_port1 Germany Germany_port1
where path id has multiple nodes and node_1_port and node_2_port represent the hops of this path.
so for example path_id = 1 start from France and end in Sweden (as per table2), passing by Italy and Belgium(as per table1), while path_id = 2 start from Belgium and end in Germany.
can you give me some hints how to proceed as I'm still new to oracle.
EDIT:
I edited the post to make it a little more realistic.
In addition, node_1 and node_2 can be random names. the order of the hops will be got from the connection of node_1 and node_2 : France is connected to Italy , then Italy connected to Belgium , then Belgium connected to Sweden , then the order of the path is : France-->Italy-->Belgium-->Sweden , knowing that the port connecting Italy to France is different than the port connecting Italy to Belgium , and both ports must be included in the final table. It's a bit complicated , I'm not able to get it right.
this is the needed output:
path_id node_name node_port order
---------------------------------------------------
1 France France_port1 1
1 Italy Italy_port1 2
1 Italy Italy_port2 3
1 Belgium Belgium_port1 4
1 Belgium Belgium_port2 5
1 Sweden Sweden_port1 6
2 Belgium Belgium_port1 1
2 Germany Germany_port1 2
This is what I reached so far , knowing that I was forced to use loops as I couldn't think of any other way.
Is there a way to get the result using a query without loops as I have around 1M records in table1.
create table table_final (path_id integer,node_name varchar2(50),node_port varchar2(50),order_rec integer);
declare
id integer :=1;
start_node varchar2(50);
end_node varchar2(50);
start_node_port varchar2(50);
end_node_port varchar2(50);
cnt integer:=1;
cnt_or integer:=1;
cnt1 integer:=0;
TYPE nodes_arr IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
nodes nodes_arr;
TYPE nodes_ports_arr IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
ports nodes_ports_arr;
id_cnt integer;
BEGIN
select max(path_id) into id_cnt from table2;
while id <= id_cnt
LOOP
select start_node,end_node,start_node_port,end_node_port into nodes(1),end_node,ports(1),end_node_port
from table2 where path_id = id;
start_node:=nodes(1);
start_node_port:=ports(1);
insert into table_final
select id,nodes(1),ports(1),cnt from dual;
commit;
while start_node!=end_node
LOOP
cnt:=cnt+1;
cnt_or:=cnt_or+1;
if cnt =2
THEN
select node_2,node2_port into nodes(cnt),ports(cnt)
from table1 where path_id=id and node_1=nodes(cnt-1) and node_2!=node_1;
ELSE
select node_2,node2_port into nodes(cnt),ports(cnt)
from table1 where path_id=id and node_1=nodes(cnt-1) and node_2!=node_1 and node_2!=nodes(cnt-2) ;
END IF;
insert into table_final
select id,nodes(cnt),ports(cnt),cnt_or from dual;
select NVL(count(*),0) into cnt1 from table1 where path_id=id and node_1=nodes(cnt) and node1_port !=ports(cnt);
if cnt1=1
THEN
cnt_or:=cnt_or+1;
insert into table_final
select id,node_1,node1_port,cnt_or
from table1 where path_id=id and node_1=nodes(cnt) and node1_port !=ports(cnt);
END IF;
start_node:=nodes(cnt);
start_node_port:=ports(cnt);
commit;
END LOOP;
cnt:=1;
cnt_or:=1;
id:=id+1;
END LOOP;
END;
I am not able to see any usage of table2 here. You can simply remove duplicates from table1 itself as following:
Select t.*,
row_number()
over (partition by path_id order by to_number(replace(node_name, 'node', ''))) as order_
From
(Select distinct path_id,
Node_name,
Node_name_1_para
From table1) t
Order by path_id, order_;
-- Update --
As per the comments, you should try this query:
Select t1.path_id,
T1.node_name
T.node_name_1_para,
Row_number() over (partition by t1.path_id order by null) as order_
From
Table1 t1 join table2 t2
On (t1.path_id = t2.path_id
And t1.node_1 between t2.start_node and t2.end_node);
Cheers!!
I have this simple table.
City Country State Id
Great Falls (null) VA 12345
Great Falls USA VA 12345
I wanted to use CONCAT function for City + Country + State with running code below in Oracle environment.
SELECT City, Country, State,
City ||', ' ||Country|| ', '||State AS Concat_fields
FROM #TEMP
City Country State Concat_fields Id
Great Falls (null) VA Great Falls, , VA 12345
Great Falls USA VA Great Falls, USA, VA 12345
Question: is there a better that the query can altered in such a way that it returns only 1 record? I wanted to show my result like this:
City Country State Concat_fields Id
Great Falls USA VA Great Falls, USA, VA 12345
Any thoughts, criticisms, suggestions?
If the only difference between the rows are null vs. non-null values, then you can use an aggregate before concatenating:
-- CTE for sample data
with "#TEMP" (city, country, state, id) as (
select 'Great Falls', null, 'VA', 12345 from dual
union all select 'Great Falls', 'USA', 'VA', 12345 from dual
)
-- actual query
select max(city) as city, max(country) as country, max(state) as state,
max(city) ||', ' ||max(country)|| ', '||max(state) as concat_fields
from "#TEMP"
group by id;
CITY COU ST CONCAT_FIELDS
----------- --- -- --------------------
Great Falls USA VA Great Falls, USA, VA
If the rows for an ID have different non-null values then that obviously won't work:
with "#TEMP" (city, country, state, id) as (
select 'Great Falls', null, 'VA', 12345 from dual
union all select 'London', 'UK', null, 12345 from dual
)
select max(city) as city, max(country) as country, max(state) as state,
max(city) ||', ' ||max(country)|| ', '||max(state) as concat_fields
from "#TEMP";
CITY CO ST CONCAT_FIELDS
----------- -- -- -------------------
London UK VA London, UK, VA
but then you would need some way to determine which row is correct - e.g. from a sequence or date column if this represents a history of addresses for a customer and you want the 'latest' version. The trick then is knowing how to determine which is 'latest'.
SELECT City, Country, State,
City ||', ' ||Country|| ', '||State AS Concat_fields
FROM #TEMP
WHERE Country IS NOT NULL
This will work for the provided dataset.
Generally speaking though Ids are unique (primary key), so make sure you are building your table in a way that adheres to more accepted database practices.
I have two tables
Table 1 - student_id number, class_type varchar2(10), class_time date, attn_time date.
Table 2 - student_id number, attendance varchar2(5), attn_recorded_time date.
I am generating a report here where I need to print all the values from table 1 and an additional info or matching value recorded_time from table 2 based on certain conditions.
SELECT a.student_id ,
a.class_type,
a.class_time,
b.attn_recorded_time
FROM student_details a
LEFT JOIN attendance_details b
ON a.student_id = b.student_id
WHERE a.class_time > b.attn_recorded_time
AND b.attn_recorded_time BETWEEN a.class_time AND (a.class_time - 1/24)
ORDER BY a.student_id,
a.class_time;
So the condition here is the class_time should always be greater than the attendance time and between the class_time and class_time - 1 hr.
I am trying to achieve the same using a merge statement
merge INTO student_details a USING attendance_details b ON (a.student_id = b.student_id)
WHEN matched THEN
update set a.attn_time = b.attn_recorded_time
where b.attn_recorded_time between a.class_time and a.class_time- 1/24;
Data for table 1
Student_id class_type class_time attn_time
1203 English 2018-09-10 11:00:00
1203 Maths 2018-09-10 11:30:00
Data for table 2
Student_id attendance attendance_recorded_time
1203 Y 2018-09-10 10:00:00
1203 Y 2018-09-10 11:00:00
1203 Y 2018-09-10 08:00:00
1203 Y 2018-09-10 09:00:00
Required data
Student_id class_type class_time attn_time
1203 English 2018-09-10 11:00:00 2018-09-10 10:00:00
1203 Maths 2018-09-10 11:30:00 2018-09-10 11:00:00
even though there are multiple data available for the same student_id 1203, I need to retrieve the latest attendance_recorded_time based on the class_time
How to achieve the above output and what am I doing wrong here?
thanks for your time.
In your where condition put lower value first (in between) or do it like here:
demo
select *
from student_details a
join attendance_details b using (student_id)
where class_time - interval '1' hour <= attn_recorded_time
and attn_recorded_time < class_time
Please try like below to get the desired output.
SELECT t.your_column
FROM
(SELECT DISTINCT a.Student_id,
MAX(b.attendance_recorded_time),
a.class_type,
MAX(a.class_time)
FROM student_details
LEFT JOIN b
ON a.student_id = b.student_id
WHERE a.class_time > b.attn_recorded_time
AND b.attn_recorded_time BETWEEN a.class_time AND (a.class_time - 1/24)
GROUP BY attendance_recorded_time,
class_time
ORDER BY a.student_id,
a.class_time
) ;
This question already has answers here:
Update rows in one table with data from another table based on one column in each being equal
(5 answers)
Closed 9 years ago.
i have table called Student with columns uniquename, age,department,city,Homecountry and another table called Employee with columns uniquename, exp,qualification, Homecountry.
now i want to update Student table's department column with Employee table's qualification column values under the where condition Student.uniquename = Employee.uniquename and Student.Homecountry = Employee.Homecountry.
please help me to write the update statement.
This kind of query is called a correlated sub query. For your requirement, the query would be as below....
update students s
set s.department = (
select e.qualification
from employee e
where s.uniquename = e.uniquename
and s.Homecountry = e.Homecountry
);
updating this post based on your replies below.
Again, going forward, always post the create table and insert statements (and the expected results) to reproduce your case. If you don't see the expected results or if you see an erro when you execute the query, post the exact message instead of just saying "not working". Here is the results of my sqlplus session.
---create table and insert statements
create table student(
name varchar2(20),
age number,
department varchar2(3),
HomeCountry varchar2(10)
);
Table created.
create table employee5(
name varchar2(20),
exp number,
qualification varchar2(3),
homecountry varchar2(10)
);
Table created.
insert into student values ('Mohan',25,'EEE','India');
insert into student values ('Raja',27,'EEE','India');
insert into student values ('Ahamed',26,'ECE','UK');
insert into student values ('Gokul',25,'IT','USA');
commit;
insert into employee5 values ('Mohan',25,'ECE','India');
insert into employee5 values ('Raja',24,'IT','India');
insert into employee5 values ('Palani',26,'ECE','USA');
insert into employee5 values ('Sathesh',29,'CSE','CANADA');
insert into employee5 values ('Ahamed',28,'ECE','UK');
insert into employee5 values ('Gokul',29,'EEE','USA');
commit;
Before updating the data...
SQL> select * from student;
NAME AGE DEP HOMECOUNTR
-------------------- ---------- --- ----------
Mohan 25 EEE India
Raja 27 EEE India
Ahamed 26 ECE UK
Gokul 25 IT USA
SQL> select * from employee5;
NAME EXP QUA HOMECOUNTR
-------------------- ---------- --- ----------
Mohan 25 ECE India
Raja 24 IT India
Palani 26 ECE USA
Sathesh 29 CSE CANADA
Ahamed 28 ECE UK
Gokul 29 EEE USA
Update statement and results
1 update student s set s.age =
2 ( select e.exp
3 from employee5 e
4 where e.name = s.name
5 and e.homecountry = s.homecountry
6* )
SQL> /
4 rows updated.
SQL> select * from student;
NAME AGE DEP HOMECOUNTR
-------------------- ---------- --- ----------
Mohan 25 EEE India
Raja 24 EEE India
Ahamed 28 ECE UK
Gokul 29 IT USA
SQL> commit;
Commit complete.
update student s
set s.age = (select e.exp
from employee5 e
where e.name = s.name
and e.homecountry = s.homecountry
and rownum < 2
)
where s.age in (select age from employee5)
It wont show the message subquery returns more than one record