Updating a table using Case statements in SQL - sql

I am trying to add a 0, 1, or null to a column in a specific category where a relativepersonid of a person has a diagdate up to a person's servicedate. Here are my tables:
DROP TABLE ICDCodes_w;
GO
CREATE TABLE ICDCodes_w
(
AnxietyDisorder VARCHAR(6),
DepressiveDisorder VARCHAR(6),
PTSD VARCHAR(6)
);
INSERT INTO ICDCodes_w
(
AnxietyDisorder,
DepressiveDisorder,
PTSD
)
VALUES
('293.84', '296.2', '309.81'),
('300', '296.21', 'F43.1'),
('305.42', 'F11.28', 'F31.76'),
('305.81', 'F43.8', 'F31.78'),
('F40.00', 'F43.10', '305.52');
GO
DROP TABLE DiagHX_w;
GO
CREATE TABLE DiagHX_w
(
ArchiveID VARCHAR(10),
RelativePersonID VARCHAR(10),
ICDCode VARCHAR(6),
DiagDate DATE
);
INSERT INTO DiagHX_w
(
ArchiveID,
RelativePersonID,
ICDCode,
DiagDate
)
VALUES
('1275741', '754241', '293.84', '1989-01-03'),
('2154872', '754241', '293.84', '1995-04-07'),
('4587215', '754241', '998.4', '1999-12-07'),
('4588775', '711121', 'F11.28', '2001-02-07'),
('3545455', '711121', NULL, NULL),
('9876352', '323668', '400.02', '1988-04-09'),
('3211514', '112101', 'F31.78', '2005-09-09'),
('3254548', '686967', 'F40.00', '1999-12-31'),
('4411144', '686967', '305.52', '2000-01-01'),
('6548785', '99999999','F40.00', '2000-02-03');
GO
DROP TABLE PatientFlags_w;
GO
CREATE TABLE PatientFlags_w
(
PersonID VARCHAR(10),
RelativePersonID VARCHAR(10),
AnxietyDisorder VARCHAR(2),
DepressiveDisorder VARCHAR(2),
PTSD VARCHAR(2),
);
INSERT INTO PatientFlags_w
(
PersonID,
RelativePersonID
)
VALUES
('99999999', '754241'),
('88888888', '754241'),
('77777777', '754241'),
('66666666', '711121'),
('55555555', '711121'),
('44444444', '323668'),
('33333333', '112101'),
('22222222', '686967'),
('11111111', '686967'),
('32151111', '887878'),
('78746954', '771125'),
('54621333', '333114'),
('55648888', '333114');
GO
DROP TABLE Person_w;
GO
CREATE TABLE Person_w
(
PersonID VARCHAR(10),
ServiceDate date
);
INSERT INTO Person_w
(
PersonID,
ServiceDate
)
VALUES
('99999999', '2000-12-31'),
('88888888', '2000-11-01'),
('69876541', '2000-09-04'),
('66666666', '2000-01-15'),
('55555555', '2000-07-22'),
('44444444', '2000-07-20'),
('65498711', '2000-11-17'),
('22222222', '2000-09-02'),
('11111111', '2000-02-04'),
('32151111', '2000-02-17'),
('78746954', '2000-03-29'),
('54621333', '2000-08-22'),
('55648888', '2000-10-20');
Here is my update statement:
UPDATE a
SET AnxietyDisorder = CASE
WHEN ICDCode IN
(
SELECT AnxietyDisorder FROM
Project..ICDCodes_w
) THEN
1
ELSE
0
END,
DepressiveDisorder = CASE
WHEN ICDCode IN
(
SELECT DepressiveDisorder FROM
Project..ICDCodes_w
) THEN
1
ELSE
0
END,
PTSD = CASE
WHEN ICDCode IN
(
SELECT PTSD FROM Project..ICDCodes_w
) THEN
1
ELSE
0
END
FROM PatientFlags_w a
JOIN DiagHX_w b
ON a.relativepersonid = b.RelativePersonID
JOIN Person_w p
ON a.personid = p.PersonID
WHERE diagdate <= p.servicedate;
This works on some values, but there are some that don't get updated. I know the issue is with my case statement and probably a join issue. What is a better way to write this? Here is an example query I used to check. The PTSD column should have a 1.
SELECT * FROM project..patientflags_w a
JOIN project..diaghx_w b
ON a.relativepersonid = b.RelativePersonID
JOIN project..person_w p
ON a.personid = p.personid
WHERE b.icdcode IN (SELECT PTSD FROM Project..ICDCodes_w)
AND b.diagdate <= p.servicedate
I did ask this question the other day, but my sample tables were all messed up, so I've verified that they work this time.

At first glance, the problem with your query is that you update the target (PatientFlags_w) multiple times: once for each flag. In some cases you seem to be ending up with the correct result, but its just by luck.
It's hard to tell if you want one row per person in the flag table, or one row per flag.
Can you review these queries and let us know if they are close to your desired results:
-- If you want one row per Person:
select RelativePersonID,
[AnxietyDisorder] = max(case when c.AnxietyDisorder is not null then 1 else 0 end),
[DepressiveDisorder] = max(case when c.DepressiveDisorder is not null then 1 else 0 end),
[PTSD] = max(case when c.PTSD is not null then 1 else 0 end)
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD)
group
by RelativePersonID;
-- If you want one row per Flag:
select RelativePersonID,
d.ICDCode,
[AnxietyDisorder] = case when c.AnxietyDisorder is not null then 1 else 0 end,
[DepressiveDisorder] = case when c.DepressiveDisorder is not null then 1 else 0 end,
[PTSD] = case when c.PTSD is not null then 1 else 0 end
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD);
If the diagnoses are not related to each other (I assumed since they are in the same table), you might want this instead:
select RelativePersonID,
[AnxietyDisorder] = max(case when c.AnxietyDisorder = d.ICDCode then 1 else 0 end),
[DepressiveDisorder] = max(case when c.DepressiveDisorder = d.ICDCode then 1 else 0 end),
[PTSD] = max(case when c.PTSD = d.ICDCode then 1 else 0 end)
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD)
group
by RelativePersonID;

Related

Oracle Stored Procedure not loading data as expected

In testing, I did the following (which worked):
create table testdata as select * from sometable;
create procedure testload as begin
delete from testdata;
insert into testdata select * from sometable;
end;
I've used the same structure for a more complex query. If I run "insert into testdata select [new query]", it will load the data into the table. However, when I created a new stored procedure using the new query, it will execute, but won't actually load data.
Note that this query was handed off to me, I didn't write it myself. The results have been validated, so while I appreciate any and all advice on how to clean up the code, I'm primarily interested in getting the stored procedure up and running with the code as close to what is below as possible.
select
CLAIM_ID
,INTERNAL_ID
,line
,VENDOR_GROUP
,case when (CLAIM_FORMAT_RULE+ POS_RULE + POS_TYPE_RULE+ POS_CODE_RULE+ MODIFIERS_RULE+ BILL_TYPE_RULE+ ATTENDING_PROV_RULE+ REFERRING_PROV_RULE) = 8 then 'Y' else 'N' end AUTH_FLAG
from
(
SELECT
AP.CLAIM_ID
,CLM1.INTERNAL_ID
, px.line
, DAT.VENDOR_GROUP
,MAX(CASE WHEN DAT.FORMAT_C IS NULL THEN 1 WHEN REPLACE(REPLACE(AP.CLAIM_FORMAT_C,1,'CMS'),2,'UB') = UPPER(DAT.FORMAT_C) THEN 1 else 0 END) AS CLAIM_FORMAT_RULE
,MAX(CASE WHEN DAT.POS_ID IS NULL THEN 1 WHEN EAF.INTERNAL_ID = DAT.POS_ID THEN 1 else 0 end ) AS POS_RULE
,MAX(CASE WHEN DAT.POS_TYPE_C IS NULL THEN 1 WHEN NVL(PX.POS_TYPE_C,0) NOT IN (20,23) THEN 1 WHEN CAST(PX.POS_TYPE_C AS VARCHAR(10)) = DAT.POS_TYPE_C THEN 1 else 0 END) AS POS_TYPE_RULE
,MAX(CASE WHEN DAT.PROC_CODE IS NULL THEN 1 WHEN DAT.PROC_CODE LIKE '%-%' AND EAP.PROC_CODE BETWEEN STRTOK(DAT.PROC_CODE,'-',1) AND STRTOK(DAT.PROC_CODE,'-',2) THEN 1 WHEN EAP.PROC_CODE = DAT.PROC_CODE THEN 1 else 0 END) AS POS_CODE_RULE
,MAX(CASE WHEN DAT.MODIFIER IS NULL THEN 1 WHEN DAT.MODIFIER = PX.MODIFIERS THEN 1 else 0 END) AS MODIFIERS_RULE
,MAX(CASE WHEN DAT.BILL_TYPE IS NULL THEN 1 WHEN DAT.BILL_TYPE LIKE '%-%' AND AP.TYPE_OF_BILL BETWEEN STRTOK(DAT.BILL_TYPE,'-',1) AND STRTOK(DAT.BILL_TYPE,'-',2) THEN 1 WHEN AP.TYPE_OF_BILL = DAT.BILL_TYPE THEN 1 else 0 END) AS BILL_TYPE_RULE
,MAX(CASE WHEN DAT.ATTENDING_PROV_NETWORK IS NULL THEN 1 WHEN DAT.ATTENDING_PROV_NETWORK = ALV.NET_AFFIL_LEVEL_C THEN 1 else 0 END) AS ATTENDING_PROV_RULE
,MAX(CASE WHEN DAT.REFERRING_PROV_NETWORK IS NULL THEN 1 WHEN DAT.REFERRING_PROV_NETWORK = RLV.NET_AFFIL_LEVEL_C THEN 1 else 0 END) AS REFERRING_PROV_RULE
FROM HCCLSC.AP_CLAIM AP
INNER JOIN HCCLSC.CLM_MAP CLM1 ON AP.CLAIM_ID=CLM1.CID --AND CLM1.INTERNAL_ID in ('363137412', '363149130')
INNER JOIN HCCLSC.CLARITY_VENDOR VEN ON AP.VENDOR_ID = VEN.VENDOR_ID
INNER JOIN HCCLSC.VENDOR_TAX_ID VTIN ON AP.VENDOR_ID = VTIN.VENDOR_ID
INNER JOIN KPBISC_GRP_NCAR.AUTH_EXCEPTIONS DAT ON VTIN.TAX_ID = DAT.VENDOR_TIN
LEFT JOIN HCCLSC.AP_CLAIM_PX PX ON AP.CLAIM_ID = PX.CLAIM_ID
LEFT JOIN HCCLSC.CLARITY_EAP EAP ON EAP.PROC_ID = PX.PROC_ID
LEFT JOIN HCCLSC.EAF_MAP EAF ON AP.LOC_ID = EAF.CID
LEFT JOIN HCCLSC.CLARITY_SER_NETAFF ALV ON AP.ATTEND_PROV_ID = ALV.PROV_ID AND ALV.LINE = 1
LEFT JOIN HCCLSC.CLARITY_SER_NETAFF RLV ON AP.REF_PROV_ID = RLV.PROV_ID AND ALV.LINE = 1
where
TRUNC(AP.DATE_RECEIVED) BETWEEN sysdate-60 AND sysdate and
COALESCE(AP.WORKFLOW_C,0)=0
and AP.STATUS_C in (2, 4, 3)
GROUP BY AP.CLAIM_ID,CLM1.INTERNAL_ID, px.line, DAT.VENDOR_GROUP
order by AP.CLAIM_ID , px.line
)
If it's not the commit problem mentioned in the other answer, try running the elect statement on it's own, but wrapping the whole statement in a select count(*) from (your_statement);
This will tell you if your query is returning zero rows.

TSQL Select only the highest credentail

I have a query that is returning multiple line for a single service because an individual may have multiple credentials. In the medical field you retain several credentials but for simplicity sake I will use just standard credentials Phd, MA, MS, BA, BS, AS
I need to know the simplest way to ignore rows where Z_ServiceLedger.clientvisit_id has any Credentials.credentials lower in the hierarchy. So if an employee does a service and he has a Phd and a MA only return the lines for Phd and if he has a Phd an Ma and a BA only return the lines for phd. We have around 50 credentials so if I use CASE for each credential you can see how mess that will get an I'm hoping there is a better way to avoid that.
Here is my current query:
SELECT DISTINCT
SUM(CASE WHEN v.non_billable = 0 THEN v.duration ELSE 0 END) / 60 AS billable_hours,
SUM(CASE WHEN (v.non_billable = 0 AND Z_ServiceLedger.payer_id = 63) THEN v.duration ELSE 0 END) / 60 AS billable_mro_hours,
Credentials.credentials
FROM
Z_ServiceLedger
INNER JOIN
ClientVisit v ON Z_ServiceLedger.clientvisit_id = v.clientvisit_id
LEFT JOIN
Employees ON v.emp_id = Employees.emp_id
LEFT JOIN
EmployeeCredential ON Employees.emp_id = EmployeeCredential.emp_id
LEFT JOIN
Credentials ON Credentials.credential_id = EmployeeCredential.credential_id
WHERE
v.rev_timein <= CASE
WHEN EmployeeCredential.end_date IS NOT NULL
THEN EmployeeCredential.end_date
ELSE GETDATE()
END
AND v.rev_timein >= #param1
AND v.rev_timein < DateAdd(d, 1, #param2)
AND Z_ServiceLedger.amount > 0
AND v.splitprimary_clientvisit_id IS NULL
AND v.gcode_primary_clientvisit_id IS NULL
AND v.non_billable = 0
AND v.non_billable = 'FALSE'
AND v.duration / 60 > 0
AND Z_ServiceLedger.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
AND (EmployeeCredential.is_primary IS NULL OR EmployeeCredential.is_primary != 'False')
AND v.client_id != '331771 '
GROUP BY
Credentials.credentials,
v.non_billable
ORDER BY
Credentials.credentials
Some aliases and formatting really shed some light on some major logical flaws here. You have at least two predicates in your where clause that logically turn a left join into an inner join. This is total shot in the dark since from both of your questions today we don't have anything to actually work with for tables or sample data.
The biggest concern though is your where clause is trying to get rows v.non_billable = 0 and where it equals 'FALSE'. It can't be both.
Select sum(Case When v.non_billable = 0 Then v.duration Else 0 End) / 60 As billable_hours
, sum(Case When (v.non_billable = 0 And sl.payer_id = 63) Then v.duration Else 0 End) / 60 As billable_mro_hours
, c.credentials
From Z_ServiceLedger sl
Inner Join ClientVisit v On sl.clientvisit_id = v.clientvisit_id
Left Join Employees e On v.emp_id = e.emp_id
Left Join EmployeeCredential ec On e.emp_id = ec.emp_id
--if you leave these predicates in the where clause you have turned your left join into an inner join.
AND v.rev_timein <= isnull(ec.end_date, GetDate())
and (ec.is_primary Is Null Or ec.is_primary != 'False')
Left Join Credentials c On c.credential_id = ec.credential_id
Where v.rev_timein >= #param1
And v.rev_timein < DateAdd(day, 1, #param2)
And v.splitprimary_clientvisit_id Is Null
And v.gcode_primary_clientvisit_id Is Null
--you need to pick one value for v.non_billable. It can't be both 0 and 'FALSE' at the same time.
And v.non_billable = 0
And v.non_billable = 'FALSE'
--And v.duration / 60 > 0
and v.duration > 60 --this is the same thing and is SARGable
And sl.amount > 0
And sl.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
And v.client_id != '331771 '
Group By c.credentials
, v.non_billable
Order By c.credentials
EDIT: Modified query to add a CTE to calculate the credential_rank, using a FROM (VALUES (...)) table-value-constructor syntax. This works in SQL 2008+. (https://learn.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql?view=sql-server-2017)
SQL Fiddle
First, I'll build out a very simple piece of data.
SETUP:
CREATE TABLE Employees ( emp_id int, emp_name varchar(20) ) ;
INSERT INTO Employees (emp_id, emp_name)
VALUES (1,'Jay'),(2,'Bob')
;
CREATE TABLE Credentials ( credential_id int, credentials varchar(20), credential_rank int ) ;
INSERT INTO Credentials (credential_id, credentials, credential_rank)
VALUES (1,'BA',3),(2,'MA',2),(3,'PhD',1)
;
CREATE TABLE EmployeeCredential (emp_id int, credential_id int, is_primary bit, end_date date )
INSERT INTO EmployeeCredential (emp_id, credential_id, is_primary, end_date)
VALUES
( 1,2,null,'20200101' )
, ( 1,3,0,'20200101' ) /* NON-PRIMARY */
, ( 1,1,1,'20100101' ) /* EXPIRED CRED */
, ( 2,3,null,'20200101' )
, ( 2,3,1,'20200101' )
;
CREATE TABLE z_ServiceLedger ( payer_id int, clientvisit_id int, amount int, action_type varchar(50) ) ;
INSERT INTO z_ServiceLedger ( payer_id, clientvisit_id, amount, action_type )
VALUES (63,1,10,'XXXXX'),(63,2,20,'XXXXX'),(63,3,10,'XXXXX'),(63,4,30,'XXXXX')
;
CREATE TABLE ClientVisit ( clientvisit_id int, client_id int, non_billable bit, duration int, emp_id int , rev_timein date, splitprimary_clientvisit_id int, gcode_primary_clientvisit_id int ) ;
INSERT INTO ClientVisit ( clientvisit_id, client_id, non_billable, duration, emp_id, rev_timein, splitprimary_clientvisit_id, gcode_primary_clientvisit_id )
VALUES
(1, 1234, 0, 110, 1, getDate(), null, null )
, (2, 1234, null, 120, 1, getDate(), null, null )
, (3, 1234, 1, 110, 2, getDate(), null, null )
, (4, 1234, 0, 130, 2, getDate(), null, null )
;
MAIN QUERY:
; WITH creds AS (
SELECT c.credential_id, c.credentials, r.credential_rank
FROM Credentials c
LEFT OUTER JOIN (VALUES (1,3),(2,2),(3,1) ) r(credential_id, credential_rank)
ON c.credential_id = r.credential_id
)
SELECT DISTINCT
SUM(CASE WHEN ISNULL(v.non_billable,1) = 0 THEN v.duration ELSE 0 END)*1.0 / 60 AS billable_hours,
SUM(CASE WHEN (ISNULL(v.non_billable,1) = 0 AND zsl.payer_id = 63) THEN v.duration ELSE 0 END)*1.0 / 60 AS billable_mro_hours,
s2.credentials
FROM Z_ServiceLedger zsl
INNER JOIN ClientVisit v ON zsl.clientvisit_id = v.clientvisit_id
AND v.rev_timein >= #param1
AND v.rev_timein < DateAdd(d, 1, #param2)
AND v.splitprimary_clientvisit_id IS NULL
AND v.gcode_primary_clientvisit_id IS NULL
AND ISNULL(v.non_billable,1) = 0
AND v.duration*1.0 / 60 > 0
AND v.client_id <> 331771
INNER JOIN (
SELECT s1.emp_id, s1.emp_name, s1.credential_id, s1.credentials, s1.endDate
FROM (
SELECT e.emp_id, e.emp_name, c.credential_id, c.credentials, ISNULL(ec.end_date,GETDATE()) AS endDate
, ROW_NUMBER() OVER (PARTITION BY e.emp_id ORDER BY c.credential_rank) AS rn
FROM Employees e
LEFT OUTER JOIN EmployeeCredential ec ON e.emp_id = ec.emp_id
AND ISNULL(ec.is_primary,1) <> 0 /* I don't think a NULL is_primary should be TRUE */
LEFT OUTER JOIN creds c ON ec.credential_id = c.credential_id
) s1
WHERE s1.rn = 1
) s2 ON v.emp_id = s2.emp_id
AND v.rev_timein <= s2.endDate /* Credential not expired at rev_timein */
WHERE zsl.amount > 0
AND zsl.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
GROUP BY s2.credentials
ORDER BY s2.credentials
Results:
| billable_hours | billable_mro_hours | credentials |
|----------------|--------------------|-------------|
| 1.833333 | 1.833333 | MA |
| 2.166666 | 2.166666 | PhD |
A couple of things to watch for:
1) Integer Division : duration/60 will return an integer. So if you had duration=70, then you'd have 70/60 = 1. You'd miss that 10 minutes, because of the result will be converted back to an integer. You lose that extra 10 minutes. Probably not what you inteded. The easiest solution is to just multiply duration by 1.0 so that it is forced into a decimal datatype and won't cause the operation to be treated like integers.
2) EmployeeCredential.is_primary != 'False' : Rather than account for the strings of "True"/"False", you should use an actual boolean value (1/0). And a NULL value should indicate that the value is NOT TRUE or FALSE rather than implying TRUE. Also, in SQL, != will work to indicate NOT EQUAL TO, but you should use <> instead. It means the same thing, but is grammatically more correct for SQL.
3) v.non_billable = 0 AND v.non_billable = 'FALSE' : This can be shortened to ISNULL(v.non_billable,1)=0 to short-circuit both checks, especially since non_billable can be NULL. You also avoid the implicit type converstion when comparing the number 0 and the string 'False'.
4) v.client_id != '331771 ' : Change to v.client_id<>33171. First, the != to <> that I mentioned earlier. Then '331771' is implicitly converted to a number. You should avoid implicit conversions.
5) You originally had v.non_billable in your GROUP BY. Since you aren't including it in your SELECT, you can't use it to GROUP BY. Also, you're already filtering out everything other than non_billable=0, so you'd never have more than one value to GROUP BY anyway. Just exclude it.
6) CASE WHEN EmployeeCredential.end_date IS NOT NULL THEN EmployeeCredential.end_date ELSE GETDATE() END : This is the same as saying ISNULL(EmployeeCredential.end_date,GETDATE()).
7) Unless you actually need to filter out specific records for a specific reason, more your JOIN conditions into the JOIN rather than using them in the WHERE clause. This will help you be more efficient with the data your initial query returns before it is filtered or reduced. Also, when using a WHERE filter with a LEFT JOIN, you may end up with unexpected results.

How to deal with two tables with huge data?

I'm facing severe issue with one table having 2 crores of data and other table having service type data which is joining with huge data table
.
Already I have created Clustered index ,Primary key and NonClustered with covering indexes .I have modifed Varchar columns to Char for space purpose I have used all filter conditions
My table looks like :
(2 crores data)
CREATE TABLEA (
ApplicationNO Varchar(25)PK,
Service_type VARCHAR(10),
service_id VARCHAR(10),
ISActive Char(1))
(300 records)
CREATE TABLEB(
service_name Varchar(25),
service_id VARCHAR(10),
service_type VARCHAR(10),
Department_id Char(3))
My Query :
Select A.Servicename,
Count(A.ApplicationNo),
sum(case when p.IS_Active='Y' then 1 else 0 end )as'Complted',
sum(case when p.IS_Active='N' then 1 else 0 end )as'InComplete'
from TableA A INNER JOIN TableB B(Service Data)
ON A.ServiceId = B.ServiceId AND A.ServiceType = B.ServiceType
Where DateFilters fromDate and ToDate AND S.ISActive = 'Y'
After creating indexes still it is taking lot of time I have tried every other way.
Please suggest me possible way.
Use this Code:
EquateA.Service_Id = B.Service_id instead of A.ServiceId = B.ServiceName
As service name not match with Service id
Use this code:
Select A.Servicename,
Count(A.ApplicationNo),
sum(case when p.IS_Active='Y' then 1 else 0 end )as'Complted',
sum(case when p.IS_Active='N' then 1 else 0 end )as'InComplete'
from TableA A INNER JOIN TableB B(Service Data)
ON A.ServiceId = B.Service_id AND A.ServiceType = B.ServiceType
Where DateFilters fromDate and ToDate AND S.ISActive = 'Y'

How do I flatten a table for an entire population?

I'm currently working to replace an update process which currently iterates over a very large table using a PL/SQL cursor, updating several columns with flattened data.
The query is structured such that the flattened results can only return a single row, by limiting to term and id. The term_eff column indicates when an activity should start appearing in results, but there is no current limitation for an end date. How can I return the flattened results of the test_activity table for all rows in the test_person table?
Test case tables:
create table test_person (id number,term varchar2(6));
create table test_activity(id number,term_eff varchar2(6),activity varchar2(10));
insert into test_person values(1,'201001');
insert into test_person values(1,'201101');
insert into test_person values(1,'201102');
insert into test_person values(2,'201001');
insert into test_person values(2,'201101');
insert into test_person values(2,'201102');
insert into test_activity values (1,'201001','Jump');
insert into test_activity values (1,'201001','Play');
insert into test_activity values (1,'201102','Run');
insert into test_activity values (2,'201001','Jump');
insert into test_activity values (2,'201101','Play');
insert into test_activity values (2,'201101','Run');
commit;
Here is the current query to return a single row. Would like a version of this that can return values for all rows in the test_person table.
select Max(CASE WHEN A.activity_rank = 1 THEN A.activity ELSE NULL END) AS activity1,
Max(CASE WHEN A.activity_rank = 2 THEN A.activity ELSE NULL END) AS activity2,
Max(CASE WHEN A.activity_rank = 3 THEN A.activity ELSE NULL END) AS activity3
from (SELECT id,
term_eff,
activity,
row_number() OVER (PARTITION BY ID ORDER BY term_eff desc) AS activity_rank
FROM test_activity
WHERE id = 1
AND term_eff <= '201001') A;
Edit: Expected results from the final query:
ID Term Activity1 Activity2 Activity3
1 201001 Jump Play
1 201101 Jump Play
1 201102 Jump Play Run
...
Basically, just remove the where condition and add a group by. You've already done the hard part:
select id,
Max(CASE WHEN A.activity_rank = 1 THEN A.activity ELSE NULL END) AS activity1,
Max(CASE WHEN A.activity_rank = 2 THEN A.activity ELSE NULL END) AS activity2,
Max(CASE WHEN A.activity_rank = 3 THEN A.activity ELSE NULL END) AS activity3
from (SELECT id, term_eff, activity,
row_number() OVER (PARTITION BY ID ORDER BY term_eff desc) AS activity_rank
FROM test_activity
WHERE term_eff <= '201001'
) A
group by id;

Converting a cursor/while loop into a set based approach

I am very new to SQL and I am trying to update a stored procedure that has a cursor in it. I had never seen a cursor prior to this one. The cursor's select statement has an inner join, but returns only a single column of IDs. The cursor calculates the number of deleted accounts for every ID, on a row by row basis.
At the end of the stored procedure, the number of deletion variables are inserted into a table
I was hoping someone that understands more about cursors/while loops would be able to suggest the best way to convert the code above into an efficient set based approach.
This is a set based way:
;WITH IDS AS
(
SELECT DISTINCT c.p_id
FROM dbo.deletion_h dh
INNER JOIN dbo.Child c
ON dh.C_id = c.c_id
WHERE CONVERT(CHAR(25),dh.delete_date,101) = #ReportDate
AND c.isT = 1
AND c.p_id NOT IN (SELECT p_id FROM dbo.Parent WHERE support = 'Y')
), Data AS
(
SELECT p_id,
COUNT(*) ActiveChild,
SUM(CASE WHEN isT = 1 AND [level] <> 'H' THEN 1 ELSE 0 END) activePk8,
SUM(CASE WHEN isT = 1 AND [level] = 'H' THEN 1 ELSE 0 END) activeHS
FROM dbo.child c
WHERE [login] <> 'f'
AND EXISTS( SELECT 1 FROM IDS
WHERE p_id = c.p_id)
GROUP BY p_id
)
SELECT SUM(CASE WHEN ActiveChild > 0 THEN 1 ELSE 0 END) NumParentDeletions,
SUM(CASE WHEN activechildPk8 > 0 THEN 1 ELSE 0 END) NumDeletionsPk8,
SUM(CASE WHEN activeHS > 0 THEN 1 ELSE 0 END) NumDeletionsHS
FROM Data
You can modify the last SELECT to make it insert those values into your table.