Where two or more values match condition? - oracle

I have been asked this question;
You list county names and the surnames of the representatives if the representatives in the counties have the same surname.
and I have the following tables;
***REPRESENTATIVE***
REPI SURNAME FIRSTNAME COUNTY CONS
---- ---------- ---------- ---------- ----
R100 Gorege Larry kent CON1
R101 shneebly john kent CON2
R102 shneebly steve kent CON3
I cant seem to figure out the correct way to ask Orical to display a surname that exists more then twice and the surnames are in the same country.
I know how to ask WHERE something = something, but that's doesn't ask what I want to know.

It sounds like you want to use the HAVING clause after doing a GROUP BY
SELECT surname, county, count(*)
FROM you_table
GROUP BY surname, county
HAVING count(*) > 1;
If you really mean "more than twice" as you wrote, none of the data you'd want HAVING count(*) > 2 but then none of your sample data would be returned.
In words, this SQL statement says
Group the data into buckets by surname and county. Each distinct combination of surname and county is a separate bucket.
Count the number of rows in each bucket
Return those buckets where there are at least two rows

Related

How to find changed values in column

I have a table which looks similar to:
AcctNbr
AcctTypCD
ContractDate
Emp
WrkStLct
WrkStRgn
10001
12M
11-01-2021
John Smith
Downtown
D
10002
BCK
11-02-2021
Jane Smith
Uptown
U
10003
HPLS
11-03-2021
Bob Jones
Midtown
M
10005
VPLS
11-04-2021
Chris Ice
Downtown
D
10006
CLBV
11-12-2021
Smith John
Uptown
U
10007
TI80
11-13-2021
Joann Penn
Midtown
M
10008
M360
10-04-2021
Jim Blue
Downtown
D
My initial query is:
Select acctnbr, accttypcd, contractdate, emp, wrkstlct, wrkstrgn
from tableA
where accttypcd in ('HPLS', 'VPLS')
and contractdate between trunc(sysdate,'mm') and sysdate
order by wrkstrgn, wrkstlct, emp, contractdate;
End users are requesting now a report which pulls back any time AcctTypCD changes from any value (a list of up to 80+ different values) to either 'HPLS' or 'VPLS' and the emp who made the change, what would be the best way to accomplish this?
I apologize in advance for any initial mistakes in this question or if this is a duplicate, first time asking a question.
Its hard to determine fully what you want to achieve given that every row has a different AcctNbr, but your description says "If acctnbr 10007 changes from ...". I will assume that a change means a new row in the table. On that assumption, you could do something like
Select acctnbr, accttypcd, contractdate, emp, wrkstlct, wrkstrgn,
lag(accttypcd) over ( partition by acctnbr order by contractdate)
from tableA
where accttypcd in ('HPLS', 'VPLS')
and contractdate between trunc(sysdate,'mm') and sysdate
order by wrkstrgn, wrkstlct, emp, contractdate;
where the LAG function will show you the previous value of 'accttypcd' where the definition of "previous" is segmented by acctnbr (the 'partition by' part) and sequenced by contractdata (the 'order by' part).
Analytic SQL (like lag, lead etc) is a big topic, so you can get a full tutorial here
https://www.youtube.com/watch?v=0cjxYMxa1e4&list=PLJMaoEWvHwFIUwMrF4HLnRksF0H8DHGtt

Oracle display value replacement of flattened, delimited foreign key values

I working on a data export for a painfully denormalized COTS product and am hung up over how to plug display values in my selection for columns that contain a delimited string of foreign keys.
Assume the following sets of data for example.
DEPARTMENTS table:
Key Value
---------------------------------
1 Finance
2 Human Resources
3 Public Affairs
4 Information Technology
PERSONNEL table:
PK FName LName Departments
-------------------------------------------------
111 Marty Graw 1|~*~|3|~*~|
222 Rick Shaw 2|~*~|4|~*~|
333 Jean Poole 4|~*~|2|~*~|3|~*~|1|~*~|
Desired output from select:
FName LName Departments
-----------------------------------------------------------------------------------
Marty Graw Finance, Public Affairs
Rick Shaw Human Resources, Information Technology
Jean Poole Information Technology, Human Resources, Public Affairs, Finance
I've found examples of how to deal with delimited strings but nothing that really seems to fit this particular scenario. Ideally I'd like to figure out how I could do it without having to create functions etc. as my permissions are pretty limited.
This will not preserve the original order of the IDs, but if that's not important then this will work:
select DISTINCT
p.fname
,p.name
,LISTAGG(d.value, ', ')
WITHIN GROUP (ORDER BY d.value)
OVER (PARTITION BY p.pk)
AS departments_list
from personnel p
left join departments d
on INSTR('|~*~|'||p.departments||'|~*~|'
,'|~*~|'||d.key||'|~*~|') > 0;
SQL Fiddle: http://sqlfiddle.com/#!4/d292e/3/0
EDIT
If you really need them listed in the same order as the IDs, you can use this variant:
select DISTINCT
p.fname
,p.lname
,LISTAGG(d.value, ', ')
WITHIN GROUP (
ORDER BY INSTR('|~*~|'||p.departments||'|~*~|'
,'|~*~|'||d.key||'|~*~|'))
OVER (PARTITION BY p.pk) AS departments_list
from personnel p
left join departments d
on INSTR('|~*~|'||p.departments||'|~*~|'
,'|~*~|'||d.key||'|~*~|') > 0;
http://sqlfiddle.com/#!4/d292e/4

Alternativ to multiple AND conditions in oracle

I know there is IN as alternative to multiple ORs:
select loginid from customer where code IN ('TEST1','TEST2','TEST3','TEST4'))
This will return all loginids with code that mach any of the four TEST elements.
Is there something similar for AND? I will need to find out all loginids that have code: TEST10,TESTA,TEST1,TESTB,AIFK,AICK....(there are 20 codes)
You cannot compare that. With ORs or IN you look for records that match one of the values. With AND you would look for a record where the column matches all those values, but this field can of course only hold one value, so will never find any record.
So obviously you are looking for something entirely else. Probably you want to aggregate your data, to find IDs for which records for each of the values exist. This would be:
select loginid
from customer
where code IN ('TEST1','TEST2','TEST3','TEST4')
group by loginid
having count(distinct code) = 4;
You could do the following:
SELECT loginid
FROM customer
WHERE code IN ('TEST1', ... , 'TEST20')
GROUP BY loginid
HAVING COUNT(DISTINCT code) = 20;
The difference between Jon's answer and this one is that if you use other codes in the table, my query will return all loginids for which there are rows for these 20 codes, and Jon's answer will return all loginids for which there are 20 distinct codes.
No
A short answer, I know, but sometimes it's the only real answer.
However
Depending on what exactly you're trying to achieve, you may be able to use a count of a query grouping the items, to check that all match.
This assumes that your CustomerCodes are kept in a separate relational table.
SELECT loginID
FROM Customer
WHERE loginID IN (
SELECT loginID, count(*) as codeCount
FROM CustomerCodes
GROUP BY loginID
HAVING codeCount = 20
)
It doesn't work if you have Code1, Code2 Code3 etc fields... you'd have to split the data out into a separate table, for example:
loginID | code
---------------
1 | code1
1 | code2

Sqlite query to determine gender by first name

I have 2 sqlite3 tables :
FND is a Table of names and their likely gender i.e.:
nm,gndr <-column names
Aliyah,F
Moses,M
Peter,M
Members is second table i.e.
Fname,Lname <-column names
DAVID X, BAKER
MARY MIA,MCGEE
TINA HEATHER,JOHNSON
JIM PETER TOM, SANTINO
The members table has first and middle names in the fname column.
I am trying to write a query to list the Members table fnames column, with a generated column indicating gender based on the first word in the fname column.
I tried this but it didn't work:
select m.fname,(select gndr from FND where upper(nm) like m.fname||'%')as gender
from Members m
can anyone correct my sql statement?
... upper(nm) like m.fname||'%'
Let's look at some example values:
nm: 'David'
fname: 'DAVID X'
SQL: 'DAVID' LIKE 'DAVID X%'
This obviously does not match.
You have to reverse the LIKE operands:
m.fname LIKE nm||'%'

Select all rows from SQL based upon existence of multiple rows (sequence numbers)

Let's say I have table data similar to the following:
123456 John Doe 1 Green 2001
234567 Jane Doe 1 Yellow 2001
234567 Jane Doe 2 Red 2001
345678 Jim Doe 1 Red 2001
What I am attempting to do is only isolate the records for Jane Doe based upon the fact that she has more than one row in this table. (More that one sequence number)
I cannot isolate based upon ID, names, colors, years, etc...
The number 1 in the sequence tells me that is the first record and I need to be able to display that record, as well as the number 2 record -- The change record.
If the table is called users, and the fields called ID, fname, lname, seq_no, color, date. How would I write the code to select only records that have more than one row in this table? For Example:
I want the query to display this only based upon the existence of the multiple rows:
234567 Jane Doe 1 Yellow 2001
234567 Jane Doe 2 Red 2001
In PL/SQL
First, to find the IDs for records with multiple rows you would use:
SELECT ID FROM table GROUP BY ID HAVING COUNT(*) > 1
So you could get all the records for all those people with
SELECT * FROM table WHERE ID IN (SELECT ID FROM table GROUP BY ID HAVING COUNT(*) > 1)
If you know that the second sequence ID will always be "2" and that the "2" record will never be deleted, you might find something like:
SELECT * FROM table WHERE ID IN (SELECT ID FROM table WHERE SequenceID = 2)
to be faster, but you better be sure the requirements are guaranteed to be met in your database (and you would want a compound index on (SequenceID, ID)).
Try something like the following. It's a single tablescan, as opposed to 2 like the others.
SELECT * FROM (
SELECT t1.*, COUNT(name) OVER (PARTITION BY name) mycount FROM TABLE t1
)
WHERE mycount >1;
INNER JOIN
JOIN:
SELECT u1.ID, u1.fname, u1.lname, u1.seq_no, u1.color, u1.date
FROM users u1 JOIN users u2 ON (u1.ID = u2.ID and u2.seq_no = 2)
WHERE:
SELECT u1.ID, u1.fname, u1.lname, u1.seq_no, u1.color, u1.date
FROM users u1, thetable u2
WHERE
u1.ID = u2.ID AND
u2.seq_no = 2
Check out the HAVING clause for a summary query. You can specify stuff like
HAVING COUNT(*) >= 2
and so forth.

Resources