How to create an Oracle SQL statement in the following case? - sql

I have an oracle table with three clumns and three data records as shown below
ID FIRST_NAME LAST_NAME
01 Jason Martin
02 Alison Mathews
03 Robert Black
I want to select the first name by a given ID.
If the ID exists, i.e., it is a value among 01, 02, and 03, the corresponding FIRST_NAME needs to be displayed;
If the ID does not exist, a * symbol needs to be displayed.
Could you help me creating such an oracle SQL statement?
Many thanks in advance.

Do an outer join, this example using id = 2:
select coalesce(FIRST_NAME, '*')
from dual
left join tablename on id = 2

Use a CASE statement:
SELECT ID,
CASE WHEN ID IN ('01', '02', '03') THEN FIRST_NAME ELSE '*' END
FROM yourTable
Note: I assumed, based on your input data, that the ID column is VARCHAR and not numeric because of the leading zeroes. If ID is numeric, then change the query to this:
SELECT ID,
CASE WHEN ID IN (1, 2, 3) THEN FIRST_NAME ELSE '*' END
FROM yourTable

Related

SQL UPDATE - How to update a column from one table using another table?

I will try to explain this as best as possible and I do apologize in advance if this does not make sense. I have two columns (First_Name and Last_Name) from one table (Table.a) where a lot of the data are incorrect. I am trying to update those columns with the correct data from another table (Table.b). The issue is that the column for table.b contain both the first name and last name data in one column. For example, table.a column and value is First_Name = Richard and Last_Name = Johnsondev while table.b column and value is Full_Name = Johnsondev, Richard. Is there a way to update table.a two columns using portions of table.b data? The only consistent is that table.b last name ends with 'dev,' like "Johnsondev, Richard"
EDIT:
I am using Microsoft SQL Server. Hopefully the below information is helpful!
Current Table Data
TABLE.B
FULL_NAME
Johnsondev, Richard
Smithdev, Kevin
TABLE.A
FIRST_NAME / LAST_NAME
Richard / Jacksondev
Kevin / Standev
Expected Output using a query
TABLE.B stays the same
TABLE.A
FIRST_NAME / LAST_NAME
Richard / Johnsondev
Kevin / Smithdev
If you use the JOIN method in tlk27's answer, this would be the syntax for SQL Server:
UPDATE a SET FIRST_NAME=SUBSTRING(b.FULL_NAME, CHARINDEX(',',b.FULL_NAME) +1, Len(b.FULL_NAME)),
LAST_NAME=LEFT(b.FULL_NAME, CHARINDEX(',',b.FULL_NAME)-1)
FROM Table_A a JOIN Table_B b ON a.ID = b.ID -- assumes a common ID field
In MySQL you could do something like below. Depending on your setup this might need modification. However, for LAST_NAME we are selecting everything up to the specified delimiter ','. For FIRST_NAME we are selecting everything to the right of what we used to get last name. Note, this will update all the names. You could use WHERE after `
UPDATE Table_A a
SET a.FIRST_NAME = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(FULL_NAME, ',', 2), ' ', -1)
FROM TABLE_B),
a.LAST_NAME = (SELECT SUBSTRING_INDEX('Johnsondev, Richard', ',', 1)
FROM TABLE_B),
Alternatively, you might need to join them together.
UPDATE Table_A a
JOIN Table_B b ON b.ID = a.ID -- assumes a common ID field
SET a.FIRST_NAME = (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(FULL_NAME, ',', 2), ' ', -1)
FROM TABLE_B),
a.LAST_NAME = (SELECT SUBSTRING_INDEX('Johnsondev, Richard', ',', 1)
FROM TABLE_B),
EDIT
Per your request, after some testing it looks like these could be alternatives in SQL Server.
SUBSTRING: SUBSTRING ( expression ,start , length )
CHARINDEX: CHARINDEX ( expressionToFind , expressionToSearch [ , start_location ] )
SELECT SUBSTRING('Johnsondev, Richard', CHARINDEX(',','Johnsondev, Richard') +1, Len('Johnsondev, Richard')) -- Richard
SELECT LEFT('Johnsondev, Richard',CHARINDEX(',','Johnsondev, Richard')-1) -- Johnsondev
With your variables:
SELECT SUBSTRING(FULL_NAME, CHARINDEX(',',FULL_NAME) +1, Len(FULL_NAME))
FROM TABLE_B
SELECT LEFT(FULL_NAME, CHARINDEX(',',FULL_NAME)-1)
FROM TABLE_B
I am not too familiar with SQL Server syntax for UPDATE but you can find it here.

Nested query that requires the first result to be returned

I have 2 tables as such
Table ErrorCodes:
type_code desc
01 Error101
02 Error99
03 Error120
Table ErrorXML:
row_index typeCode
1 87
2 02
3 01
The output should be the description(column desc) of the first matched type_code between the 2 tables
Expected output : Error99
I have gotten so far.
select isnull(descript, 'unknown') as DESCRIPTION
from (select top 1 a.stmt_cd as descript
from ErrorCodes a, ErrorXML b
where a.type_cd = b.typecode
order by b.row_index)
But this query doesn't return the string UNKNOWN when there is no common typecode (join condition) between the 2 tables. In this case, im getting null.
How can I resolve this?
This is an interesting question. I believe the following can be an intuitive and beautiful solution (I used desc_ as column name rather than desc which is a reserved word):
select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_
from ErrorXML a
where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null
order by row_index
limit 1;
If you also need to handle the case if query returns no row then for MySQL, following syntax should suffice. For other databases you can use similar encapsulation with isnull, nvl, etc:
select ifnull((select (select desc_ from ErrorCodes x where x.type_code = a.typeCode) desc_ from ErrorXML a where (select desc_ from ErrorCodes x where x.type_code = a.typeCode) is not null order by row_index limit 1), 'UNKNOWN');
To test I used following scripts and seems to work properly:
create database if not exists stackoverflow;
use stackoverflow;
drop table if exists ErrorCodes;
create table ErrorCodes
(
type_code varchar(2),
desc_ varchar(10)
);
insert into ErrorCodes(type_code, desc_) values
('01', 'Error101'),
('02', 'Error99'),
('03', 'Error120');
drop table if exists ErrorXML;
create table ErrorXML
(
row_index integer,
typeCode varchar(2)
);
insert into ErrorXML(row_index, typeCode) values
('1', '87'),
('2', '02'),
('3', '01');
Final-1 quote: While generating your tables try to use same column names as much as possible. I.e. I'd suggest ErrorXML to use type_code rather than typeCode.
Final quote: I choose to use lower letters in SQL since capital letters should be used while emphasizing an important point. I also suggest that style.
What about this: Do a subquery to bring back the first row_index for each type_code.
Do a LEFT OUTER Join on the ErrorCodes table so that you get NULLs as well.
SELECT
ISNULL(ErrorCodes.desc,'unknown') AS description
ErrorXML.row_index
FROM ErrorCodes
LEFT OUTER JOIN (
SELECT type_code, MIN(row_index) AS row_index
FROM ErrorXML
GROUP BY type_code
) AS ErrorXML ON ErrorCodes.type_code = ErrorXML .type_code

Custom Ordering of SELECT Results

I'm working with a Pro*C query, but this question should be general SQL. My research has been a dead end, but I think I'm missing something.
Suppose my server has an array of students' names, {"Alice","Charlie","Bob"}. I query the Student_Ids table for the students' ID numbers:
SELECT id_no FROM student_ids
WHERE student_name IN ('Alice','Charlie','Bob');
To simplify server-side processing, I want to sort the result set in the same order as the students' names. In other words, the result set would be {alice_id_no, charlie_id_no, bob_id_no} regardless of the actual ordering of the table or the behavior of any particular vendor's implementation.
The only solution I can think of is:
. . .
ORDER BY
CASE WHEN student_name='Alice' THEN 0
WHEN student_name='Charlie' THEN 1
WHEN student_name='Bob' THEN 2 END;
but that seems extremely messy and cumbersome when trying to dynamically generate/run this query.
Is there a better way?
UPDATE I gave a terrible example by pre-sorting the students' names. I changed the names to be deliberately unsorted. In other words, I want to sort the names in a non-ASC or DESC-friendly way.
UPDATE II Oracle, but for knowledge's sake, I am looking for more general solutions as well.
The ORDER BY expression you've given for your sample data is equivalent to ORDER BY student_name. Is that what you intended?
If you want a custom ordering that is not alphabetical, I think you might have meant something like this:
ORDER BY
CASE
WHEN student_name = 'Alice' THEN 0
WHEN student_name = 'Charlie' THEN 1
WHEN student_name = 'Bob' THEN 2
END;
You can use a derived table as well, that holds the names as well as the ordering you want. This way you only have to put the names in a single time:
SELECT S.id_no
FROM
student_ids AS S
INNER JOIN (
SELECT Name = 'Alice', Seq = 0 FROM DUAL
UNION ALL SELECT 'Bob', 2 FROM DUAL
UNION ALL SELECT 'Charlie', 1 FROM DUAL
) AS N
ON S.student_name = N.Name
ORDER BY
N.Seq;
You could also put them into a temp table, but in Oracle that could be somewhat of a pain.
Can you do this?
order by student_name
To do a custom sort, you only need one case:
ORDER BY (CASE WHEN student_name = 'Alice' THEN 1
WHEN student_name = 'Bob' THEN 2
WHEN student_name = 'Charlie' THEN 3
ELSE 4
END)
why not this :
SELECT id_no FROM student_ids
WHERE student_name IN ('Alice','Bob','Charlie')
ORDER BY student_name
You can ORDER BY any columns, not necessary those in SELECT list or WHERE clause
SELECT id_no
FROM student_ids
WHERE student_name IN ('Alice','Bob','Charlie)
ORDER BY id_no;
Add a table to hold the sort priorities then you can use the sort_priorities in whatever query you want (and easily update the priorities):
CREATE TABLE student_name_sort_priorities (
student_name VARCHAR2(30) CONSTRAINT student_name_sort_priority__pk PRIMARY KEY,
sort_priority NUMBER(10) CONSTRAINT student_name_sort_priority__nn NOT NULL
CONSTRAINT student_name_sort_priority__u UNIQUE
);
(If you want two values to be equivalently sorted then don't include the UNIQUE constraint.)
INSERT INTO student_name_sort_priorities VALUES ( 'Alice', 0 );
INSERT INTO student_name_sort_priorities VALUES ( 'Charlie', 2 );
INSERT INTO student_name_sort_priorities VALUES ( 'Bob', 1 );
Then you can join the sort priority table with the student_ids table and use the extra column to perform ordering:
SELECT id_no
FROM student_ids s
LEFT OUTER JOIN
student_name_sort_priorities p
ON (s.student_name = p.student_name)
ORDER BY
sort_priority NULLS LAST;
I've used a LEFT OUTER JOIN so that if a name is not contained on the student_name_sort_priorities table then it does not restrict the rows returned from the query; NULLS LAST is used in the ordering for a similar reason - any student names that aren't in the sort priorities table will return a NULL sort priority and be placed at the end of the ordering. If you don't want this then just use INNER JOIN and remove the NULLS LAST.
How about using a 'table of varchar' type like the build-in below:
TYPE dbms_debug_vc2coll is table of varchar2(1000);
test:
SQL> select customer_id, cust_first_name, cust_last_name from customers where cust_first_name in
2 (select column_value from table(sys.dbms_debug_vc2coll('Frederic','Markus','Dieter')));
CUSTOMER_ID CUST_FIRST_NAME CUST_LAST_NAME
----------- -------------------- --------------------
154 Frederic Grodin
149 Markus Rampling
152 Dieter Matthau
That seems to force the order, but that might just be bad luck. I'm not really a sql expert.
The execution plan for this uses 'collection iterator' instead of a big 'or' in the typical:
select customer_id, cust_first_name, cust_last_name from customers where cust_first_name in ('Frederic','Markus','Dieter');
hth, Hein.

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 returns Column has "FristName LastName" as 1 string how to order by Lastname?

I'm using Oracle. My sql returns a column like this
Name:
John Smith
David Lee
...
If I do Order by Name, it will order by first name. How do I order by Last name? If I do Order by Lastname, Firstname oracle returns invalid identifiers. I tried substr, instr but it doesn't work. I know the sql is tedious but just want the data to quickly fix this issue.
Full SQL:
http://pastebin.com/hYkdHBDM
You say your SQL "returns a column" in that format. Do you mean the column is stored that way, or that it's stored as two fields and composed into one in the SQL statement?
If stored that way it's difficult to create an algorithm that will reliably determine what part of a multi-part name is the last name part (indeed, this is sometimes down to personal preference of the person owning the name).
If stored in two separate fields you should be able to ORDER BY LastName, FirstName depending on how the SQL is constructed and whether there are any intermediate views between you and the table. Please post the SQL and table structure.
First, in order to sort by LastName, it needs to be one of the columns you return in each of the queries in your Union All. Second, you can greatly simplify your query by using a common-table expression. Third, do not use the comma delimited syntax for Joins (e.g. From TableA, TableB, TableC...). Instead use the ISO Join syntax.
With RootQuery As
(
Select MeetingID
, FirstName || ' ' || LastName AS Name
, LastName
, CASE WHEN RSVP = 1 THEN 1 ELSE NULL END AS Yes
, CASE WHEN RSVP = 0 THEN 1 ELSE NULL END AS No
, CASE WHEN RSVP = 2 THEN 1 ELSE NULL END AS Phone
, CASE WHEN RSVP = -1 THEN 1 ELSE NULL END AS No_Reply
, MysteryTable0.Response1
, MysteryTable1.Response2
, Note
, groupname
From Attendance A
Join Allusers As B
And B.MemberId = A.PersonId
Join MembershipGroups As M
And M.MemberId = B.MemberID
Join (
SELECT TD.MEMBERID AS MEM0
, Response AS Response1
FROM TRACKINGDETAILS TD, ALLUSERS U
Where TD.MEMBERID = U.MEMBERID
And TD.TRACKINGID = 64
) MysteryTable0
On MysteryTable0.Mem0 = B.MemberId
Join (
SELECT TD.MEMBERID AS MEM1
, Response AS Response2
FROM TRACKINGDETAILS TD, ALLUSERS U
Where TD.MEMBERID = U.MEMBERID
And TD.TRACKINGID = 65
) As MysteryTable1
On MysteryTable1.Mem1 = B.MemberId
Where Meetingid = :1
)
Select MeetingId, Name, LastName, Yes, No, Phone, No_Reply
, Response1, Response2
, Note, GroupName
From RootQuery
Union All
Select Null, 'Total', LastName, SUM(Yes), SUM(No), SUM(Phone), SUM(No_Reply)
, TO_CHAR(SUM(Response1))
, TO_CHAR(SUM(Response2))
, NULL, Groupname
From RootQuery
Group By GroupName
Union All
Select Null, 'Grand Total', LastName, SUM(Yes), SUM(No), SUM(Phone), SUM(No_Reply)
, TO_CHAR(SUM(Response1))
, TO_CHAR(SUM(Response2))
,NULL, ' '
From RootQuery
Group By ???
Order By GroupName Desc, LastName Asc, Name Asc
Btw, the last query will probably have a problem in that it did not have a Group By (which I denoted with Group By ???) but you are using aggregate functions.
What Matthew PK said is correct however he failed to mention that INSTR can parse backwards in which case, his "fail" scenario would be resolved.
Here try this:
create table test_name (f_name varchar2(20), l_name varchar2(20), full_name varchar2(20));
insert into test_name (f_name, l_name, full_name) values ('John', 'Mellencamp', 'John 2Mellen');
insert into test_name (f_name, l_name, full_name) values ('John', 'Mellencamp', 'John C. 1Mellen');
select f_name, l_name, substr(full_name,instr(full_name,' ',-1,1)) as substr, full_name from test_name order by substr(full_name,instr(full_name,' ',-1,1));
Basically the money shot is: substr(full_name,instr(full_name,' ',-1,1))
If you know the field will always be "FirstName LastName" separated by a space you could:
ORDER BY RIGHT(Name, INSTR(Name,' '))
This is the number of characters, on the right side, starting at the space.
This will fail if any other names are separated by a space like "John Cougar Mellencamp"