oracle sql how to match name split through 3 columns? - sql

This database is not mine so i cannot alter it to a single name column (unfortunately).
**table1:**
name1,name2,name3
john,mega,rambo
john,master,travolta
john,super,connor
i'm sending query string $qs to a php file via ajax
file.php?qs=john%rambo
i want it to query the oracle database and return the name "John MEga Rambo", so i thought this would work but it doesn't:
SQL:
SELECT CONCAT(CONCAT(name1,name2),name3) as name
FROM table1
WHERE name LIKE '%{$qs}%'
I guess the biggest problem here is that someone can search by name1 or name2 / name1 or name3. Any ideas ?

Presumably you're getting an invalid identifier error. You can't referance a column alias in the same level of query it's defined (except in order by), because of the order the query is parsed and executed.
You could use a CTE or an inline view (subquery):
SELECT name
FROM (
SELECT CONCAT(CONCAT(name1,name2),name3) as name
FROM table1
)
WHERE name LIKE '%{$qs}%'
With some dummy data:
create table table1 (name1 varchar2(10), name2 varchar2(10), name3 varchar2(10));
insert into table1 (name1, name2, name3) values ('john', 'mega', 'rambo');
insert into table1 (name1, name2, name3) values ('john', 'master', 'travolta');
insert into table1 (name1, name2, name3) values ('john', 'super', 'connor');
that query - with {$qs} replaced with rambo - gets:
NAME
------------------------------
johnmegarambo
As you can see concat won't automatically add whitespace, so you might find the concatenation operator easier:
SELECT name
FROM (
SELECT name1 ||' '|| name2 ||' '|| name3 as name
FROM table1
)
WHERE name LIKE '%{$qs}%'
NAME
--------------------------------
john mega rambo
But if you're looking for more exact matches you might want to check each column instead:
SELECT name1 ||' '|| name2 ||' '|| name3 as name
FROM table1
WHERE name1 = '{$qs}'
OR name2 = '{$qs}'
OR name3 = '{$qs}'
Maybe you want the flexibility to search for partial matches though.
As a further consideration, by default you can only search for values exactly matching the case of the search string - so with your data, searching for RAMBO wouldn't find a match. There are a couple of ways around that if it's an issue for you.

Thank you Alex Poole for the replies, those didn't quite solve the problem BUT it pointed me in the right direction! It seems like oracle is a little bit picky with syntax, so a simple HAVING that would work in MySQL won't work on oracle. I'll post both solutions for ORACLE and MySQL
MySQL:
SELECT CONCAT(CONCAT(name1,name2),name3) AS name
FROM table1
HAVING name LIKE '{%$qs}%'
ORACLE
WITH table1 as ( SELECT CONCAT(CONCAT(name1,name2),name3) as name
FROM table1)
select * from table1 where name like '%{$qs}%'

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.

Get the row values as column in SQL

I have below table,and need to get row values as an output.
This is a part of a view in Oracle Database.
I need to get the output using SQL as below.name,address,regionare taking from another table by referringID .
Looking for much simple way since full query have more than 15 columns and below also need to be added as columns.
Thanks.
"Looking for much simple way since full query have more than 15 columns"
Sorry, you can have a complex query or no query at all :)
The problem is the structure of the posted table mandates a complex query. That's because it uses a so-called "generic data model", which is actually a data anti-model. The time saved in not modelling the requirement and just smashing values into the table is time you will have to spend writing horrible queries to get those values out again.
I assume you need to drive off the other table you referred to, and the posted table contains attributes supplementary to the core record.
select ano.id
, ano.name
, ano.address
, ano.region
, t1.value as alt_id
, t2.value as birth_date
, t3.value as contact_no
from another_table ano
left outer join ( select id, value
from generic_table
where key = 'alt_id' ) t1
on ano.id = t1.id
left outer join ( select id, value
from generic_table
where key = 'birth_date' ) t2
on ano.id = t2.id
left outer join ( select id, value
from generic_table
where key = 'contact_no' ) t3
on ano.id = t3.id
Note the need to use outer joins: one of the problems with generic data models is the enforcement of integrity constraints. Weak data typing can also be an issue (say if you wanted to convert the birth_date string into an actual date).
PIVOT concept fits well for these types of problems :
SQL> create table person_info(id int, key varchar2(25), value varchar2(25));
SQL> create table person_info2(id int, name varchar2(25), address varchar2(125), region varchar2(25));
SQL> insert into person_info values(4150521,'contact_no',772289317);
SQL> insert into person_info values(4150522,'alt_id','98745612V');
SQL> insert into person_info values(4150522,'birth_date',date '1990-04-21');
SQL> insert into person_info values(4150522,'contact_no',777894561);
SQL> insert into person_info2 values(4150521,'ABC','AAAAAA','ASD');
SQL> insert into person_info2 values(4150522,'XYZ','BBBBB','WER');
SQL> select p1.id, name, address, region, alt_id, birth_date, contact_no
from person_info
pivot
(
max(value) for key in ('alt_id' as alt_id,'birth_date' as birth_date,'contact_no' as contact_no)
) p1 join person_info2 p2 on (p1.id = p2.id);
ID NAME ADDRESS REGION ALT_ID BIRTH_DATE CONTACT_NO
------- ------- ------- ------ --------- ---------- ----------
4150521 ABC AAAAAA ASD 12345678V 21-APR-89 772289317
4150522 XYZ BBBBB WER 98745612V 21-APR-90 777894561

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.

SQL Where clause

My application initially had a query similar to this one:-
SELECT column_name from PERSON
WHERE name in (list);
where list is comma separated list.
But, now the requirement has changed and i have to query the Persons table with name and age given.
I have the nameAgeList.
Initially, i thought a query similar to this would work (Create nameList and ageList from nameAgeList)
SELECT column_name from Person
WHERE name in (nameList)
AND age in (ageList)
But after carefully thinking, this seems to be a wrong query.
Please let me know how should I proceed ahead with this query.
Under Oracle, you can do this:
SELECT * FROM Person
WHERE
(name, age) IN (
('name1', age1),
('name2', age2)
-- Etc...
)
You can have up to 1000 tuples in this list.
One option is to create a temporary table (or if SQL Server, a table variable), place your names and ages in this table, and then simply join to it:
SELECT column_name from Person p
INNER JOIN myTempTable t ON t.Name = p.Name AND t.age = p.age
It's not pretty, and this one only works when you can generate your statement in code:
SELECT column from Person
WHERE 1=1
AND ( ( name = name1 and age = age1 )
OR ( name = name2 and age = age2 )
OR ( name = name3 and age = age3 )
OR ( name = name4 and age = age4 )
OR ( name = name5 and age = age5 )
... et cetera
)
Now, if you could put those lists into tables you could do alot better than this. Is there any way you can get those lists into the database? I assume that you really need some Person table that holds name and age for each individual.

setting up a default value of a column in select statement

actually i have 2 tables table1 and table2
table1
name
city
addr.
table2
name
city
addr.
ph.no
now ph.no field is an extra field in table 2
so i want to show field ph.no with a default value of 12345 in the output of select query on table1 as i want to append that output into an outfile.
help me out ..I am using db2 as400 database
Yes, you can do this:
SELECT name, city, addr, 12345 AS ph_no
FROM table1
I know this thread is very old but in case someone needs an answer to Nimmagadda's question, you should be able to accomplish it with something along the lines of:
SELECT name, city, addr,
CASE name
WHEN 'john' THEN 12345
WHEN 'peter' THEN 123
ELSE 0 /* ???? */ END AS ph_no
FROM table1