Retrieving rows randomly in pl/sql query - sql

I have a table (t1). I know how to retrieve percentage of set randomly.
What I want is to insert 30% of randomly selected rows into t2, and insert remaining 70% into table t3.
Is there any other way except inserting 30% into table t2 and than compare t2 with t1 and insert into t3? This method is not good for me since table is huge.
ps. oracle version - 11g

Look into ora_hash. Generate a hash using the table's PK (or some similar column combination) with a bucket of 9, and those with a 0-6 go in one table, and those with 7,8 or 9 go in another.

would an insert all work? here is one I did with the HR employees table so I ordered by random and took 30 percent of them. those ones got an indicator of one. I did a union all on the whole table and give it an indicator of 0. I took the max for the indicator then did an insert all. if the indicator is 1 into the first table otherwise the remaining 70% into the second.
INSERT ALL
WHEN (table_one_ind = 1) THEN
INTO table_one
(
employee_id,
first_name,
last_name,
email,
hire_date,
job_id
)
VALUES
(
employee_id,
first_name,
last_name,
email,
hire_date,
job_id
)
ELSE
INTO table_two
(
employee_id,
first_name,
last_name,
email,
hire_date,
job_id
)
VALUES
(
employee_id,
first_name,
last_name,
email,
hire_date,
job_id
)
SELECT MAX (table_one_ind) table_one_ind,
employee_id,
first_name,
last_name,
email,
hire_date,
job_id
FROM
(SELECT t.*,
1 AS table_one_ind
FROM
( SELECT * FROM employees ORDER BY dbms_random.value
) t
WHERE rownum <=
( SELECT ceil(COUNT(*)*.3) FROM employees
)
UNION ALL
SELECT t.*, 0 FROM employees t
)
GROUP BY employee_id,
first_name,
last_name,
email,
hire_date,
job_id

Related

How is this query write correctly?

I have a query like below. I want to create a table and convert null values to 0. But my query failed. How can I write it correctly ?
CREATE TABLE TABLE_2
AS
SELECT *
FROM TABLE_1 1 = 2;
INSERT INTO TABLE_2 (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER,
HIRE_DATE, JOB_ID, SALARY, NVL(COMMISSION_PCT, 0), MANAGER_ID, DEPARTMENT_ID)
SELECT *
FROM TABLE_1;
Both your statements contain syntax errors.
The CREATE TABLE ... SELECT AS statement requires a valid query: your query is missing the WHERE keyword.
CREATE TABLE TABLE_2
AS
SELECT *
FROM TABLE_1 where 1 = 2;
Insert statements can have two projections. The optional project is the list of columns in the target table. Your list includes NVL(COMMISSION_PCT, 0) which is not a valid column name, but a declaration of the data manipulation you wish to apply to the column from the source table. Consequently you need to include it in the projection of the SELECT statement.
INSERT INTO TABLE_2
SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER,
HIRE_DATE, JOB_ID, SALARY, NVL(COMMISSION_PCT, 0), MANAGER_ID, DEPARTMENT_ID
FROM TABLE_1;
I have published a working demo on db<>fiddle.
INSERT INTO TABLE_2(EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY,COMMISSION_PCT,MANAGER_ID, DEPARTMENT_ID)
SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, NVL(COMMISSION_PCT,0), MANAGER_ID, DEPARTMENT_ID
FROM TABLE_1

SQL query to compare multiple columns in same table in oracle

I have a requirement to find emplid having data difference in same table. Table consist of 50-60 columns.. I need to check if any column has change in data from previous row, emplidshould get pick up as well as if any new employee get add that also needs to pick up..
I have created a basic query and it is working but need some way to achieve same purpose as I do not want to write every column name.
My query:
select
emplid
from
ps_custom_tbl t, ps_custom_tbl prev_t
where
prev_t.emplid = t.emplid
and t.effdt = (select max effdt from ps_custom_tbl t2
where t2.emplid = t.emplid)
and prev_t.effdt = (select max(effdt) from ps_custom_tbl prev_t2
where emplid = prev_t.emplid and effdt < t.effdt)
and (t.first_name prev_t.first_name Or t.last_name prev_t.last_name …. 50 columns);
Can you please suggest another way to achieve same thing?
You can use MINUS.
if no_data then both are the same, if there are some records - mean that there is a difference between
create table emp as select * from hr.employees;
insert into emp select employee_id+1000, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id,
decode(department_id ,30,70, department_id)
from hr.employees;
select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id
from emp where employee_id <= 1000
minus
select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id
from emp where employee_id > 1000;
But you have to list all columns, because if you have eg different dates or ids - they will be compared too. But it's easier to list columns in SELECT clause then write for everyone WHERE condition.
Maybe it will help.
-- or if different tables and want to compare all cols simply do
drop table emp;
create table emp as select * from hr.employees;
create table emp2 as
select employee_id, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id,
decode(department_id ,30,70, department_id) department_id
from hr.employees;
select * from emp
minus
select * from emp2;
---- ADD DATE CRITERIA
-- yes, you can add date criteria and using analytical functions check which
-- is newer and which is
older and then compare one to another. like below:
drop table emp;
create table emp as select * from hr.employees;
insert into emp
select
employee_id,
first_name,
last_name,
email,
phone_number,
hire_date+1,
job_id,
salary,
commission_pct,
manager_id,
decode(department_id ,30,70, department_id)
from hr.employees;
with data as --- thanks to WITH you retrieve data only once
(select employee_id, first_name, last_name, email, phone_number,
hire_date,
row_number() over(partition by employee_id order by hire_date desc) rn -- distinguish newer and older record,
job_id, salary, commission_pct, manager_id, department_id
from emp)
select employee_id, first_name, last_name, email, phone_number, department_id from data where rn = 1
MIUNUS--- find the differences
select employee_id, first_name, last_name, email, phone_number, department_id from data where rn = 2;
You will have to write all columns in some sense no matter what you do.
In terms of comparing current and previous, you might find this easier
select
col1,
col2,
...
lag(col1) over ( partition by empid order by effdt ) as prev_col1,
lag(col2) over ( partition by empid order by effdt ) as prev_col2
...
and then you comparison will be along the lines of
select *
from ( <query above >
where
decode(col1,prev_col1,0,1) = 1 or
decode(col2,prev_col2,0,1) = 1 or
...
The use of DECODE in this way handles the issues of nulls.
My requirement is to send out data to managers, they change any/all/none of the data in the columns, and send back to me. I then have to identify each column that has a difference from what I sent, and mark those columns as changed for a central office reviewer to visually scan and approve/deny the changes for integration back into the central data set.
This solution may not fit your needs of course, but a template structure is offered here that you can augment to meet your needs no matter the number of columns. In the case of your question, 50-60 columns will make this SQL query huge, but I've written heinously long queries in the past with great success. Add columns a few at a time rather than all wholesale according to this template and see if they work along the way.
You could easily write pl/sql to write this query for you for the tables in question.
This would get very cumbersome if you had to compare columns from 3 or more tables or bi-directional changes. I only care about single direction changes. Did the person change my original row columns or not. If so, what columns did they change, and what was my before value and what is their after value, and show me nothing else please.
In other words, only show me rows with columns that have changes with their before values and nothing else.
create table thing1 (id number, firstname varchar2(10), lastname varchar2(10));
create table thing2 (id number, firstname varchar2(10), lastname varchar2(10));
insert into thing1 values (1,'Buddy', 'Slacker');
insert into thing2 values (1,'Buddy', 'Slacker');
insert into thing1 values (2,'Mary', 'Slacker');
insert into thing2 values (2,'Mary', 'Slacke');
insert into thing1 values (3,'Timmy', 'Slacker');
insert into thing2 values (3,'Timm', 'Slacker');
insert into thing1 values (4,'Missy', 'Slacker');
insert into thing2 values (4,'Missy', 'Slacker');
commit;
Un-comment commented select * queries one at a time after each data set to understand what is in each data set at each stage of the refinement process.
with rowdifferences as
(
select
id
,firstname
,lastname
from thing2
minus
select
id
,firstname
,lastname
from thing1
)
--select * from rowdifferences
,thing1matches as
(
select
t1.id
,t1.firstname
,t1.lastname
from thing1 t1
join rowdifferences rd on t1.id = rd.id
)
--select * from thing1matches
, col1differences as
(
select
id
,firstname
from rowdifferences
minus
select
id
,firstname
from thing1matches
)
--select * from col1differences
, col2differences as
(
select
id
,lastname
from rowdifferences
minus
select
id
,lastname
from thing1matches
)
--select * from col2differences
,truedifferences as
(
select
case when c1.id is not null then c1.id
when c2.id is not null then c2.id
end id
,c1.firstname
,c2.lastname
from col1differences c1
full join col2differences c2 on c1.id = c2.id
)
--select * from truedifferences
select
t1m.id
,case when td.firstname is not null then t1m.firstname end beforefirstname
,td.firstname afterfirstname
,case when td.lastname is not null then t1m.lastname end beforelastname
,td.lastname afterlastname
from thing1matches t1m
join truedifferences td on t1m.id = td.id
;

Finding Duplicate Rows in a Table

I am trying to find out how many duplicate records I have in a table. I can use count, but I'm not sure how best to eliminate records where the count is only 1.
select first_name, last_name, start_date, count(1)
from employee
group by first_name, last_name, start_date;
I can try to order by the count, but I am still not eliminating those with a count of one.
you can use having clause as having Count(*) > 1 after group by like this :
select
first_name,
last_name,
start_date,
Count(*) AS Count
from
employee
group by
first_name,
last_name,
start_date
having
Count(*) > 1

Calculate salary difference between two rows in HIVE

I have a table with below columns-
last_name, first_name, department, salary
I want to calculate list of employees who receive a salary less than 100, compared
to their immediate employee with higher salary in the same department. I went to below answer- Compute differences between succesive records in Hadoop with Hive Queries and tried but I think I am doing something wrong as I am new to HIVE.
Below is the query which I am running-
select last_name,first_name, salary from emp where
100 = LEAD(salary,1) OVER(PARTITION BY department ORDER BY salary)-salary;
Please help me with the solution.
Use a case expression.
SELECT last_name,
first_name,
salary
FROM (SELECT last_name,
first_name,
salary,
CASE
WHEN 100 > LEAD(salary, 1)
OVER(
PARTITION BY department
ORDER BY salary) - salary THEN 1
ELSE 0
END sal_flag
FROM emp)
WHERE sal_flag = 1;
Hive enforces every sub query to be given a name. I have just added the name to Kaushik's query. Try this, it will work.
SELECT last_name,
first_name,
salary
FROM (SELECT last_name,
first_name,
salary,
CASE
WHEN 100 > LEAD(salary, 1)
OVER(
PARTITION BY department
ORDER BY salary) - salary THEN 1
ELSE 0
END sal_flag
FROM employee) v
WHERE sal_flag = 1;
I personally prefer using WITH clause as opposed to subquery as below. With clauses make the query more readable. Also, they produce better execution plan generally.
WITH sal_view
AS (SELECT last_name,
first_name,
salary,
CASE
WHEN 100 > LEAD(salary, 1)
OVER(
PARTITION BY department
ORDER BY salary) - salary THEN 1
ELSE 0
END sal_flag
FROM employee)
SELECT last_name,
first_name,
salary
FROM sal_view
WHERE sal_flag = 1;
Try
with temp as(
select last_name,
first_name,
department,
salary,
LEAD(salary, 1)
OVER( PARTITION BY department
ORDER BY salary) as diff
FROM emp
)
select ast_name,
first_name,
department,
salary
from temp
where diff >100

How to use distinct in third column

I want to run the query
select first_name, last_name, distinct salary from employees
But it throws an error. While if I use this select distinct salary, first_name, last_name from employees it runs.
I want o/p in the form of first column should be first_name then last_name then distinct salary.
try this!
SELECT Salary, First_Name, Last_Name
FROM table_name
GROUP BY Salary
the above should return a list of first_name and last_name of people who share the same salary.
If your data set contains duplicate rows you may want to do this to get rid of duplicate rows:
WITH salaries
AS ( SELECT DISTINCT Salary,
First_Name,
Last_Name
FROM table_name )
SELECT Salary,
First_Name,
Last_Name
FROM salaries
GROUP BY Salary;
Here's the trick, you should insert first to temp table where you distinct the salary, then after that , you can now select the data in your temp table with your desired arrangement of columns.
select distinct salary, first_name, last_name * into #temp from employees
Then after the distinct, you can now do what you want in the second query without the distinct.
select first_name, last_name, salary from #temp