Get name for different status with same ID in SQL - sql

I have a dataset with ID_1 and ID_2 who are individuals and a different STATUS for same ID_1 and ID_2 like this:
And I want to assign the NAME with the same ID_1 and ID_2 for the second STATUS, like this:
SELECT * FROM example WHERE (status = 1 and status = 2)

If we perform a self join (Substitute your tableName in where it says "TableName" below; we join the table to itself. Each is it's own data sets and since we sometimes want the name from the "related" records; a left join is needed. Together based on ID_1, and ID_2; but only when the the status on the second table is 1 greater than the first table so all status 2 join with status 1 for the same ID_1 and ID_2. When the status is 1 then no join occurs.. We then use coalesce to return the the desired name (one from 'B' table if such a join occurred, otherwise the base name from table aliased 'A'.
SELECT A.Index, A.ID_1, A.ID_2, A.Name, coalesce(B.Name, A.Name) as Name_P
FROM TableName A
LEFT JOIN tableName B
on A.ID_1=B.ID_1
and A.ID_2=B.ID_2
and A.Status-1 = B.Status --This is changed from comment having give it more thought...
In theory (as this is untested without complete DDL and test data. This could be provided on one of my dbfiddle sites https://dbfiddle.uk/ for example.)
Index 1-7 would always return Robert for coalesce(B.Name, A.Name) [using A.Name value] because the join would find no status 0 records with ID_1 and ID_2 matching. So B.Name would be Null, and A.Name (Robert) would be returned; thus is the nature of coalesce() in this example.
For index 8-9, B.Name would have a value of Robert so it would be returned. Coalesce() returns the first non-null value in the series coalesce(value1, value2,value3, value...)
But I'm assuming several things here.
status is always separated by 1
status is numeric
there are no status 0
if status 3 exists, you'd want it related to status 2 names.
several other things which you may not have provided sample data for
several other edge cases which may result in an improper result.
We need to understand your data and situation better in order to know how to help you.
For example if index 10 "Fred" Exists with ID_1 111 and ID_2 of 1 and status 3... what should the Name_P be? Justin? Robert? neither? Both?
Now I don't' know if you're doing this to return data in a set, or if you need the Name_P as some sort of "Extra" so for now I'm returning as part of the data set. If you want more: clarify the question by providing sample data expected results and WHY those results. Where it gets challenging is defining limits on the data could you have status 3 and what happens then? or are we just working with status 1 & 2... things we need to understand
Consider the complete DDL & Fiddle with sample data to help us help you so we can test our "attempts" against data you have where you know what the expected results are. The image is ok; but DDL and a minimally complete verifiable example is better.

Related

SQL - compare multiple rows and return rows?

I have a large database and i'd like to pull info from a table (Term) where the Names are not linked to a PartyId for a certain SearchId. However:
There are multiple versions of the searches (sometimes 20-40 - otherwise I think SQL - Comparing two rows and two columns would work for me)
The PartyId will almost always be NULL for the first version for the search, and if the same Name for the same SearchId has a PartyId associated in a later version the NULL row should not appear in the results of the query.
I have 8 left joins to display the information requested - 3 of them are joined on the Term table
A very simplified sample of data is below
CASE statement? Join the table with itself for comparison? A temp table or do I just return the fields I'm joining on and/or want to display?
Providing sample data that yields no expected result is not as useful as providing data that gives an expected result..
When asking a question start with defining the problem in plain English. If you can't you don't understand your problem well enough yet. Then define the tables which are involved in the problem (including the columns) and sample data; the SQL you've tried, and what you're expected result is using the data in your sample. Without this minimum information we make many guesses and even with that information we may have to make assumptions; but without a minimum verifiable example showing illustrating your question, helping is problematic.
--End soap box
I'm guessing you're after only the names for a searchID which has a NULL partyID for the highest SearchVerID
So if we eliminated ID 6 from your example data, then 'Bob' would be returned
If we added ID 9 to your sample data for name 'Harry' with a searchID of 2 and a searchVerID of 3 and a null partyID then 'Harry' too would be returned...
If my understanding is correct, then perhaps...
WITH CTE AS (
SELECT Name, Row_Number() over (partition by Name order by SearchVersID Desc)
FROM Term
WHERE SearchID = 2)
SELECT Name
FROM CTE
WHERE RN = 1
and partyID is null;
This assigns a row number (RN) to each name starting at 1 and increasing by one for each entry; for searchID's of 2. The highest searchversion will always have a RN of 1. Then we filter to include only those RN which are 1 and have a null partyID. This would result in only those names having a searchID of 2 the highest search version and a NULL partyID
Ok So I took the question a different way too..
If you simply want all the names not linked to a PartyID for a given search.
SELECT A.*
FROM TERM A
WHERE NOT EXISTS (SELECT 1
FROM TERM B
WHERE A.Name = B.Name
AND SearchID = 2) and partyID is not null)
AND searchID = 2
The above should return all term records associated to searchID 2
that have a partyId. This last method is the exists not exists and set logic I was talking about in comments.

SQL - include results you are looking for in a column and set all other values to null

I have two tables, one with orders and another with order comments. I want to join these two tables. They are joined on a column "EID" which exists in both tables. I want all orders. I also want to see all comments with only certain criteria AND all other comments should be set to null. How do I go about this?
Orders Table
Order_Number
1
2
3
4
Comments Table
Comments
Cancelled On
Ordered On
Cancelled On
Cancelled On
In this example I would like to see for my results:
Order_Number | Comments
1 | Cancelled On
2 | Null
3 | Cancelled On
4 | Cancelled On
Thanks!
This seems like a rather trivial left join.
select o.order_number, c.comments
from orders o
left join comments c
on o.eid = c.eid
and (here goes your criteria for comments)
Tested on Oracle, there might be subtle syntax differences for other DB engines.
It depends on one condition:
Are you trying to SET the other comments to null? (replace the values in the table)
or
Are you trying to DISPLAY the other comments as null? (dont display them)
If you want to change the values in the table use
UPDATE `table` SET `column` = null WHERE condition;
otherwise use:
SELECT column FROM table JOIN othertable WHERE condition;

Oracle ORA-01427: single-row subquery returns more than one row, but actually no rows

I can already hear the groans looking at my title but please bear with me a moment. :)
I have two tables that have a few columns in common and are updated through different means. Given a specific identifier I want to update the first table with values from the second table if the first table is missing some information.
Table A looks something like:
Dept_ID Reviewer Reviewer_Team Reviewer_Code
ACM Null Null Null
EOT Null Null Null
QQQ Joe Joe's Group XYZ
ACM Null Null Null
ZZZ Null Null Null
Table B looks something like:
Dept_ID Reviewer Reviewer_Team Reviewer_Code
AAA Al Al's Group 123
BBB Bob Bob's Group 234
ZZZ Zoe Zoe's Group 567
If Reviewer_Code is Null in Table A we want to find Table A's Dept_ID in Table B, and update Table A's other fields to match Table B. Note that Table A might have multiple records with the same Dept_ID in which case we'd expect them to have the same values updated from Table B.
Sounds easy. Using the above tables as an example there are no matches in Table B, so the ACM and EOT records would not be updated at this step. Table A's ZZZ record though would get updated based on Table B's ZZZ record.
However there's a chance that there would be no matches in Table B. So pretend Table A doesn't have the ZZZ record, just the ACM and EOT that have Nulls.
I'm new to Oracle (coming from SQL Server) so maybe I'm testing this wrong, but what I have is a bunch of queries one after another in a .sql window of Oracle SQL Developer. This seems to work for me just fine normally. When it gets to this query though I get the dreaded "single-row subquery" error.
Here's the query I've tried a few different ways:
UPDATE VchrImpDetailCombined vchr
SET (Reviewer, Reviewer_Team, Reviewer_Code) =
(SELECT DISTINCT b.Reviewer, b.Reviewer_Team, b.Reviewer_Code
FROM GlobPMSDeptIdMapping b
WHERE b.Dept_Id = vchr.Dept_Id)
WHERE vchr.Reviewer_Code IS NULL
AND vchr.Business_L1 = 'CF'
AND vchr.Dept_ID IS NOT NULL;
or
UPDATE VchrImpDetailCombined vchr
SET (Reviewer, Reviewer_Team, Reviewer_Code) =
(SELECT DISTINCT b.Reviewer, b.Reviewer_Team, b.Reviewer_Code
FROM GlobPMSDeptIdMapping b
inner join VchrImpDetailCombined a
on b.Dept_Id = a.Dept_Id
WHERE b.Dept_Id = vchr.Dept_Id)
WHERE vchr.Reviewer_Code IS NULL
AND vchr.Business_L1 = 'CF'
AND vchr.Dept_ID IS NOT NULL;
I've tried a few other things as well such as doing "WHERE EXISTS SELECT blahblah", or "WHERE b.Dept_ID IS NOT NULL", etc.
Now, given my example data above, the subquery should have 0 records, keeping in mind there actually isn't a ZZZ record in Table A like my example, just the ACM and EOT. Table B simply doesn't have records with the matching Dept_ID in Table A. So my expectation would be for a 0 record update and happily moving along to the next query.
When I run these queries in a string of other queries I get the error. If I run the query all by its lonesome I simply get a "3 rows updated" which seems odd that anything is updating considering there should be no matches. But the 3 rows updated would seem to match the 3 ACM and EOT records even though Table B has nothing to update from given the criteria.
I must be missing something obvious, but I just can't seem to grasp it. There's a bajillion of these ORA-01427 questions so I was so sure I could find the answer already out there, but couldn't seem to find it.
Any ideas?
You need to instruct Oracle that it should perform the update only when there are data with which to do so (and I would have expected such to be needed for SQL Server as well, but I'm uncertain). This will overcome that obstacle, at the expense of performing an additional subquery:
UPDATE VchrImpDetailCombined vchr
SET (Reviewer, Reviewer_Team, Reviewer_Code) = (
SELECT b.Reviewer, b.Reviewer_Team, b.Reviewer_Code
FROM GlobPMSDeptIdMapping b
WHERE b.Dept_Id = vchr.Dept_Id
)
WHERE vchr.Reviewer_Code IS NULL
AND vchr.Business_L1 = 'CF'
AND vchr.Dept_ID IN (
SELECT Dept_Id
FROM GlobPMSDeptIdMapping
);
Per my comment on the question, I removed the DISTINCT from the (original) subquery, as it's either unneeded or ineffective.

in sql how to return single row of data from more than one row in the same table

I have a single table of activities, some labelled 'Assessment' (type_id of 50) and some 'Counselling' (type_id of 9) with dates of the activities. I need to compare these dates to find how long people wait for counselling after assessment. The table contains rows for many people, and that is the primary key of 'id'. My problem is how to produce a result row with both the assessment details and the counselling details for the same person, so that I can compare the dates. I've tried joining the table to itself, and tried nested subqueries, I just can't fathom it. I'm using Access 2010 btw.
Please forgive my stupidity, but here's an example of joining the table to itself that doesn't work, producing nothing (not surprising):
Table looks like:
ID TYPE_ID ACTIVITY_DATE_TIME
----------------------------------
1 9 20130411
1 v 50 v 20130511
2 9 20130511
3 9 20130511
In the above the last two rows have only had assessment so I want to ignore them, and just work on the situation where there's both assessment and counselling 'type-id'
SELECT
civicrm_activity.id, civicrm_activity.type_id,
civicrm_activity.activity_date_time,
civicrm_activity_1.type_id,
civicrm_activity_1.activity_date_time
FROM
civicrm_activity INNER JOIN civicrm_activity AS civicrm_activity_1
ON civicrm_activity.id = civicrm_activity_1.id
WHERE
civicrm_activity.type_id=9
AND civicrm_activity_1.type_id=50;
I'm actually wondering whether this is in fact not possible to do with SQL? I hope it is possible? Thank you for your patience!
Sounds to me like you only want to get the ID numbers where you have a TYPE_ID entry of both 9 and 50.
SELECT DISTINCT id FROM civicrm_activity WHERE type_id = '9' AND id IN (SELECT id FROM civicrm_activity WHERE type_id = '50');
This will give you a list of id's that has entries with both type_id 9 and 50. With that list you can now go and get the specifics.
Use this SQL for the time of type_id 9
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '9'
Use this SQL for the time of type_id 50
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '50'
Your query looks OK to me, too. The one problem might be that you use only one table alias. I don't know, but perhaps Access treats the table name "specially" such that, in effect, the WHERE clause says
WHERE
civicrm_activity.type_id=9
AND civicrm_activity.type_id=50;
That would certainly explain zero rows returned!
To fix that, use an alias for each table. I suggest shorter ones,
SELECT A.id, A.type_id, A.activity_date_time,
B.type_id, B.activity_date_time
FROM civicrm_activity as A
JOIN civicrm_activity as B
ON A.id = B.id
WHERE A.type_id=9
AND B.type_id=50;

SQL Precedence Query

I have a logging table which has three columns. One column is a unique identifier, One Column is called "Name" and the other is "Status".
Values in the Name column can repeat so that you might see Name "Joe" in multiple rows. Name "Joe" might have a row with a status "open", another row with a status "closed", another with "waiting" and maybe one for "hold". I would like to, using a defined precedence in this highest to lowest order:("Closed","Hold","Waiting" and "Open") pull the highest ranking row for each Name and ignore the others. Anyone know a simple way to do this?
BTW, not every Name will have all status representations, so "Joe" might only have a row for "waiting" and "hold", or maybe just "waiting".
I would create a second table named something like "Status_Precedence", with rows like:
Status | Order
---------------
Closed | 1
Hold | 2
Waiting | 3
Open | 4
In your query of the other table, do a join to this table (on Status_Precedence.Status) and then you can ORDER BY Status_Precedence.Order.
If you don't want to create another table, you can assign numeric precedence using a SELECT CASE
Select Name, Status, Case Status
When 'Closed' then 1
When 'Hold' then 2
When 'Waiting' then 3
When 'Open' Then 4
END
as StatusID
From Logging
Order By StatusId -- Order based on Case
A lookup table is also a good solution though.
I ended up using matt b's solution and using this final query to filter out the lower ranked (lower bing higher numbered).
SELECT * from [TABLE] tb
LEFT JOIN Status_Precedence sp ON tb.Status = sp.Status
WHERE sp.Rank = (SELECT MIN(sp2.rank)
FROM[Table] tb2
LEFT JOIN Status_Precedence sp2 ON tb2.Status = sp2.Status
WHERE tb.Status = tb2.Status)
order by tb.[name]