UPDATE and return some rows twice - sql

I try to update and return rows. The problem is I use a nested select with UNION to get some rows twice and I want to get them returned twice. Example:
Table:
First_name | last_name | ready
-----------+-----------+------
john | doe | false
| smith | false
jane | | false
Query:
With list(name) as (
Select First_name
from table1
where First_name Not null and ready=false
union
Select last_name
from table1
where last_name Not null and ready=false
)
Select * from list
This returns:
John
jane
doe
smith
Now I want to update the rows found by the select and use update ... returning instead. But the update only returns the three affected rows, while I want it to return the rows as the select in the example does. Is there any way?

Rewrite to:
WITH cte AS (
UPDATE table1
SET ready = true
WHERE (first_name IS NOT NULL OR last_name IS NOT NULL)
AND NOT ready
RETURNING first_name, last_name
)
SELECT first_name FROM cte WHERE first_name IS NOT NULL
UNION ALL
SELECT last_name FROM cte WHERE last_name IS NOT NULL;
Same result, just shorter and faster: This query accesses table1 a single time instead of three times like in your original.
(Verify the superior performance with EXPLAIN ANALYZE on a test table.)
UNION ALL like #Clodoaldo already mentioned. UNION would eliminate duplicates, which is substantially slower (and probably wrong here).

with list(name) as (
select first_name
from table1
where first_name is not null and ready=false
union all
select last_name
from table1
where last_name is not null and ready=false
), u as (
update table1
set ready = true
where
(first_name is not null or last_name is not null)
and
not ready
)
select * from list
You need union all to have the four rows. It is is [not] null

Related

Oracle SQL - break 1 row into 2 rows

I have the following table:
create table members
(
member_number number(10),
title varchar2(5),
lastname varchar2(30),
personal_email varchar2(20),
work_email varchar2(20)
)
with the data
Insert into MEMBERS
(MEMBER_NUMBER, TITLE, LASTNAME, PERSONAL_EMAIL, WORK_EMAIL)
Values
(11, 'MR', 'Dore', 'personal#email.com', 'work#email.com');
COMMIT;
select member_number, title, lastname,
decode (:pi_email,
'Work', work_email, personal_email) destination
from members
where member_number = 11
and decode (:pi_email,
'Work', work_email, personal_email) is not null
union
select member_number, title, lastname, work_email
from members
where member_number = 11
and :pi_email = 'Both'
and work_email is not null
Question:
Is it possible to re-write the code using 1 select and not using a UNION.
I want to be able to pass pi_email value of 'Work' and select the work_email, alternatively select the personal_email, however if I pass 'Both' I want to select both email addresses in 2 lines/rows.
If I understand correctly, you can unpivot the data and then use a where clause. In Oracle 12C+, you can do this using a lateral join:
select m.member_number, m.title, m.lastname, e.email
from members m cross join lateral
(select 'work' as which, m.work_email as email from dual union all
select 'personal' as which, m.personal_email from dual
) e
where :pi_email in (e.which, 'both');
In earlier versions of Oracle, you can do something similar with union all in the from clause.
How about a CTE with additional column (what in my example, which tells what kind of an e-mail address is that)?
Sample data:
SQL> select * from members;
MEMBER_NUMBER TITLE LASTNAME PERSONAL_EMAIL WORK_EMAIL
------------- ----- ---------- -------------------- --------------------
11 MR Dore personal#email.com work#email.com
Query (runs in SQL*Plus) - fetch work e-mail address:
SQL> set ver off
SQL> with data as
2 (select 'P' what, member_number, title, lastname, personal_email email
3 from members
4 union all
5 select 'W' what, member_number, title, lastname, work_email email
6 from members
7 )
8 select member_number,
9 title,
10 lastname,
11 email
12 from data
13 where member_number = &pi_memnum
14 and (what = '&&pi_email' or '&&pi_email' is null);
Enter value for pi_memnum: 11
Enter value for pi_email: W
MEMBER_NUMBER TITLE LASTNAME EMAIL
------------- ----- ---------- --------------------
11 MR Dore work#email.com
Another example, which fetches both e-mail addresses (what is left NULL):
SQL> undefine pi_email
SQL>
SQL> /
Enter value for pi_memnum: 11
Enter value for pi_email:
MEMBER_NUMBER TITLE LASTNAME EMAIL
------------- ----- ---------- --------------------
11 MR Dore personal#email.com
11 MR Dore work#email.com
SQL>
If you don't use SQL*Plus, the last two lines (i.e. the where clause) would be
where member_number = :pi_memnum
and (what = :pi_email or :pi_email is null);

Opposite of SUBSTR for big query

I have two tables in bigquery that can be matched on a ID. Unfortunately one of the ids has a prefix (3 digits that is not consistent)
For example, one ID is "12345" (Table two / id) and the second ID is "T1_12345" (Table one / Link_id)
When selecting from the first table I can just use SUBSTR to remove the prefix before working in the second table. However, if I want to first select in the second table with the shorter prefix and than in the first table I can't find a way to do that.
The code below is an example of what i'm working with.
I'm looking for something similar to the RIGHT or SUBSTR functions, but in reverse basically.
SELECT body from [table] where link_id in
(SELECT
id
FROM
[table_two]
WHERE
author == "Username")
This code isn't correct, but might give a clearer picture of what i'm trying to do.
SELECT body from [table] where "12345" in
(SELECT
"T1_12345"
FROM
[table_two]
WHERE
author == "Username")
Edit:
For example, if I had these two tables...
Table 1
| First_name| Link ID |
|-----------|-----------|
| James |T1_12345 |
| John |T2_12346 |
Table 2
| Surname| ID |
|-----------|--------|
| Tobbin |12345 |
| Peterson |12346 |
And I ran this query...
SELECT first_name from [table 1] where Link_ID in
(SELECT
ID
FROM
[table_two]
WHERE
Surname == "Peterson")
The output I want is: John Peterson
Below is for BigQuery Standard SQL
#standardSQL
SELECT first_name
FROM `project.dataset.table_one`
WHERE SUBSTR(Link_ID, 4) IN (
SELECT ID
FROM `project.dataset.table_two`
WHERE Surname = 'Peterson'
)
with result:
Row first_name
1 John
--
#standardSQL
SELECT CONCAT(first_name, ' ', Surname) full_name
FROM `project.dataset.table_one`
LEFT JOIN `project.dataset.table_two`
ON SUBSTR(Link_ID, 4) = ID
WHERE Surname = 'Peterson'
with result:
Row full_name
1 John Peterson
Below is for BigQuery Legacy SQL
#legacySQL
SELECT first_name
FROM (
SELECT First_name, SUBSTR(Link_ID, 4) short_ID
FROM [project:dataset.table_one]
)
WHERE short_ID IN (
SELECT ID
FROM [project:dataset.table_two]
WHERE Surname = 'Peterson'
)
--
#legacySQL
SELECT CONCAT(first_name, ' ', Surname) full_name
FROM (
SELECT First_name, SUBSTR(Link_ID, 4) short_ID
FROM [project:dataset.table_one]) t1
LEFT JOIN [project:dataset.table_two] t2
ON short_ID = ID
WHERE Surname = 'Peterson'
If you want to use in, can't you just use this?
SELECT body
FROM [table]
WHERE link_id IN (SELECT SUBSTR(id, 4)
FROM [table_two]
WHERE author = 'Username'
);

SELECT DISTINCT Users in Table 1 which don't exist in Table 2

I have a table with 4 columns of user information. Each table has the following columns:
Username | Full_Name | Job_Name | Current_Job_Allowed
Table 1 includes all users and the Job_Name which they have permissions to view. This means that there are multiple lines of the same username in Table 1 with different Job_Name values.
Table 2 contains a list of all possible users.
Username |Full_Name
--------------+-----------------
amunoz |Andrew Munoz
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jcarr |Jason Carr
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
Query 1 contains all users specified by a query parameter which I specify, i.e. 'KML_20160531'.
I would like to now select a DISTINCT list of all users which have a different Job_Name from the parameter I specify for Job_Name. For example Table 1 contains:
Username|Full_Name |Job_Name |Current_Job_Allowed
--------+------------+------------+----------------------
amunoz |Andrew Munoz|KML_20160531|1
jcarr |Jason Carr |KML_20160531|1
rfelipe |Ralf Felipe |KML_20140531|1
amunoz |Andrew Munoz|KML_20160431|1
I would then like to return the below when I enter 20160531 for Job_Name. This will return all possible new users for the Job_Name value I entered.
Username |Full_Name
--------------+---------------
csmith |Carl Smith
cwatkins |Cat Watkins
ggriffiths |Garmin Griffiths
jhothi |Jark Hothi
jphillips |Jim Phillips
lbradfield |Lisa Bradfield
ntaylor |Noria Taylor
rfelipe |Ralf Felipe
This parameter query will show you which users have a Table 1 row with Job_Name matching the parameter value:
PARAMETERS which_job Text ( 255 );
SELECT t1.[Username], t1.Job_Name
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job];
So you can use that as a subquery, left join Table 2 to the subquery, and select the rows where the "right side" is Null:
PARAMETERS which_job Text ( 255 );
SELECT t2.[Username]
FROM
[Table 2] AS t2
LEFT JOIN
(
SELECT t1.[Username]
FROM [Table 1] AS t1
WHERE t1.Job_Name=[which_job]
) AS sub
ON t2.[Username] = sub.[Username]
WHERE sub.[Username] Is Null;
Assuming that query returns the correct rows, add the other field you want to see to SELECT t2.[Username].
You should not need DISTINCT unless Table 2 allows duplicate Username values, or Table 1 allows more than one row with the same combination of Username and Job_Name.
You can use not in and distinct
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select distinct username
from table2 where job_name ='my_value');
and for only the job
select distinct a.username, a.fullname
from table1 as a
where a.username not in (select username
from table2
group by username
having count(job_name) = 1
and job_name ='my_value' );
You don't actually need DISTINCT for this query at all:
select t.*
from table1 as t
where not exists (select 1
from table2 as t2
where t2.username = t.username and t2.job_name = "KML_20160531"
);
From your description of the problem, Current_Job_Allowed does not seem relevant.
This will be syntax for your problem
SELECT DISTINCT UserName
FROM Table1
WHERE UserName NOT IN (
SELECT UserName From Table2
WHERE job_name = ''
);

How to write IN clause query to replace null for no value parameter

I am writing a query in which where clause have IN clause and there are large number of values in this IN clause , I want to fetch the result such that if there is no value exist in table for value given in IN clause then a raw containing 0 or null should return for that value. for example..
select age,sex,date_joining from emp where name IN ('amit','john','paul','dilip')
Now assume for this query ,data for john and paul does not exist in database then result should be like below..
21 male 21-AUG-2011
null null null
null null null
25 male 9-aug-2010
we can also have 0 instead of null if null is not possible
Thanks...
select filter.name
, emp.age
, emp.sex
, emp.date_joining
from (
values ('amit'), ('john'), ('paul'), ('dilip')
) filter(name)
left join
emp
on emp.name = filter.name
Live example at SQL Fiddle.
For older values of SQL Server, replace the line with values by:
from (
select 'amit'
union all select 'john'
union all select 'paul'
union all select 'dilip'
) filter(name)
You can also use common table expression to get this result:
;With AllEmpDetails as
(
Select [Name] from emp
UNION Select 'amit'
UNION Select 'john'
UNION Select 'paul'
UNION Select 'dilip'
)Select AllEmpDetails.Name, e2.Age, e2.Sex, e2.date_joining
from AllEmpDetails
Left Join emp e2 on e2.[Name] = AllEmpDetails.Name
In my database, I have already added details for amit and dilip so i have used UNION since you can easily get the detail about the available employees. On the other hand you can use UNION ALL with Distinct.

SQL Select multiple rows using where in one table

I have a table named "Student".
Student
id | name | age
1 | john | 10
2 | jack | 10
3 | jerry| 10
I wanna select 1 and 2 rows. I wrote that Select * from Student where name=john and name=jack
But return "Empty Set".
How do i do it. Help me.
select *
from student
where name in ('john', 'jack')
Or
select *
from student
where name = 'john'
or name = 'jack'
You need an OR rather than an AND.
Whatever conditions your write, it checks them all against each record. As no single record has both name = 'john' AND name = 'jack' they all fail.
If, instead, you use OR...
- The 1st record yields TRUE OR FALSE which is TRUE.
- The 2nd record yields FALSE OR TRUE which is TRUE.
- The 3rd record yields FALSE OR FALSE which is FALSE.
Select * from Student where name='john' OR name='jack'
Or, using a differnt way of saying it all...
SELECT * FROM Student WHERE name IN ('john', 'jack')
Use single quotes to surround your values, plus use and instead of or:
where name='john' or name = 'jack'
try this one.
declare #names varchar(100)
set #names='john,jack'
Select * from Student
where charIndex(',' + rtrim(cast(name as nvarchar(max))) + ',',',' +isnull(#names,name) +',') >0