ORACLE - FLATTEN DATA - oracle

I am sure that this question has been asked before but possibly not in this way.
Here is the data:
I need the rows flattened and the end output:
Any ideas?

As you already said we exactly can use row_number here and then use the row_number to pivot it,
select *
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
pivot
(
max(make) make,max(model) model
for rn in (1,2,3,4,5)
)
Or
Using conditional aggregation which many prefers over PIVOT
select userid
,max(case when rn = 1 then make end) make_1
,max(case when rn = 1 then model end) model_1
,max(case when rn = 2 then make end) make_2
,max(case when rn = 2 then model end) model_2
,max(case when rn = 3 then make end) make_3
,max(case when rn = 3 then model end) model_3
from
(
select userid,make,model,
row_number() over (partition by userid order by make,model) rn
from table1
)
group by userid;
In both cases you can say the disadvantage is hard coding the row numbers but this is how it works or else you may opt for dynamic SQL if necessary.

Related

ORA-00979: not a GROUP BY expression in Oracle

I can not execute this code on Oracle, the error shows:
"ORA-00979: not a GROUP BY expression"
However, I was able to run it successfully on MySQL.
How does this happen?
SELECT CONCAT(i.lname, i.fname) AS inst_name,
CONCAT(s.lname, s.fname) AS stu_name,
t.avg_grade AS stu_avg_grade
FROM(
SELECT instructor_id, student_id, AVG(grade) as avg_grade, RANK() OVER(PARTITION BY instructor_id ORDER BY grade DESC) AS rk
FROM grade
GROUP BY 1,2) t
JOIN instructor i
ON t.instructor_id = i.instructor_id
JOIN student s
ON s.student_id = t.student_id
WHERE t.rk = 1
ORDER BY 3 DESC
You can't use ordinals like GROUP BY 1,2 in Oracle. In addition, the ORDER BY grade clause inside your RANK() function has a problem. Keep in mind that analytic functions evaluate after the GROUP BY aggregation, so grade is no longer available. Here is a version which should work without error:
SELECT CONCAT(i.lname, i.fname) AS inst_name,
CONCAT(s.lname, s.fname) AS stu_name,
t.avg_grade AS stu_avg_grade
FROM
(
SELECT instructor_id, student_id, AVG(grade) AS avg_grade,
RANK() OVER (PARTITION BY instructor_id ORDER BY AVG(grade) DESC) AS rk
FROM grade
GROUP BY instructor_id, student_id
) t
INNER JOIN instructor i
ON t.instructor_id = i.instructor_id
INNER JOIN student s
ON s.student_id = t.student_id
WHERE t.rk = 1
ORDER BY t.avg_grade DESC;

How to write Oracle SQL to reset counter based on the value?

How can I reset the counter like below examples (I need to generate counter in the column named "Counter I need to generate"?
Looks like each value larger than 1 resets the counter, is that right?
If so, you could assign a group number first, based on the number of times a value > 1 occurs before the current row (including). So row 1 to 11 will be group 0, 12 and 13 will be group 1, and so on.
Then you can apply the row_number window function to generate the numbering partitioned by that group:
with VW_GROUPED as (
select
t.*,
(select count(*) from TheTable x where x.URN <= t.URN and x.GAPNOOFDAYS > 1) as GROUPNO
from
TheTable /* <- your table name here */ t)
select
g.URN,
g.CUSTOMER_ID,
g.GAPNOOFDAYS,
row_number() over (partition by GROUPNO order by URN) as "Counter I need to generate"
from
VW_GROUPED g
Here's an alternate example that generates the group in using analytic functions instead of a scalar subquery:
with grp as (
select t.*
, sum(case gapnoofdays when 1 then 0 else 1 end) over (partition by customer_id order by urn) grp
from your_table t
)
select grp.*
, row_number() over (partition by customer_id, grp order by urn) n
from grp;

Please help me to optimize the below mentioned script as the same table(i.e. Incident_Audit_log) has utilized multiple times?

select A.*
from Incident_Audit_log a where incident_audit_log_id in
(select top 1 incident_audit_log_id from Incident_Audit_log b
where b.incident_id=a.incident_id and b.status_did=a.status_did
and b.tracking_code_did = (select tracking_code_did
from Incident_Audit_log where update_date = (select MAX(update_date)
from Incident_Audit_log where Status_did in (103, 1035)
and incident_id = b.incident_id)
and incident_id = b.incident_id)
order by update_date asc)
I am not sure what you want to achieve but I guess that you want to extract row with new newest update and status_did equal to 13 and 1035.
In that case this should work:
select *
from (
select ROW_NUMBER() OVER(ORDER BY update_date DESC) AS rn,
*
from Incident_Audit_log
where status_did in (103, 1035)
) as SubQueryAlias
where rn = 1
In case not , provide more info.

How do I get rowcount of a cte in a separate dataset?

I have identified a way to get fast paged results from the database using CTEs and the Row_Number function, as follows...
DECLARE #PageSize INT = 1
DECLARE #PageNumber INT = 2
DECLARE #Customer TABLE (
ID INT IDENTITY(1, 1),
Name VARCHAR(10),
age INT,
employed BIT)
INSERT INTO #Customer
(name,age,employed)
SELECT 'bob',21,1
UNION ALL
SELECT 'fred',33,1
UNION ALL
SELECT 'joe',29,1
UNION ALL
SELECT 'sam',16,1
UNION ALL
SELECT 'arthur',17,0;
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total = ( SELECT
Count( id )
FROM cteCustomers )
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Using this technique the returned results is really really fast even on complex joins and filters.
To perform paging I need to know the Total Rows returned by the full CTE. I have "Bodged" this by putting a column that holds it
Total = ( SELECT
Count( id )
FROM cteCustomers )
Is there a better way to return the total in a different result set without bodging it into a column? Because it's a CTE I can't seem to get it into a second result set.
Without using a temp table first, I'd use a CROSS JOIN to reduce the risk of row by row evaluation on the COUNT
To get total row, this needs to happen separately to the WHERE
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
CROSS JOIN
(SELECT Count( *) AS Total FROM cteCustomers ) foo
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
However, this isn't guaranteed to give accurate results as demonstrated here:
can I get count() and rows from one sql query in sql server?
Edit: after a few comments.
How to avoid a CROSS JOIN
WITH cteCustomers
AS ( SELECT
id,
Row_Number( ) OVER(ORDER BY Age DESC) AS Row,
COUNT(*) OVER () AS Total --the magic for this edit
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT
name,
age,
Total
FROM cteCustomers
INNER JOIN #Customer cust
/*This is where I choose the columns I want to read, it returns really fast!*/
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
Note: YMMV for performance depending on 2005 or 2008, Service pack etc
Edit 2:
SQL Server Central shows another technique where you have reverse ROW_NUMBER. Looks useful
#Digiguru
OMG, this really is the wholy grail!
WITH cteCustomers
AS ( SELECT id,
Row_Number() OVER(ORDER BY Age DESC) AS Row,
Row_Number() OVER(ORDER BY id ASC)
+ Row_Number() OVER(ORDER BY id DESC) - 1 AS Total /*<- voodoo here*/
FROM #Customer
WHERE employed = 1
/*Imagine I've joined to loads more tables with a really complex where clause*/
)
SELECT name, age, Total
/*This is where I choose the columns I want to read, it returns really fast!*/
FROM cteCustomers
INNER JOIN #Customer cust
ON cust.id = cteCustomers.id
WHERE row BETWEEN ( #PageSize * #PageNumber - 1 ) AND ( #PageSize * ( #PageNumber ) )
ORDER BY row ASC
So obvious now.

sql query question

hey guys, just having a bit of difficulty with a query, i'm trying to figure out how to show the most popular naturopath that has been visited in a centre. My tables look as follows;
Patient(patientId, name, gender, DoB, address, state,postcode, homePhone, businessPhone, maritalStatus, occupation, duration,unit, race, registrationDate , GPNo, NaturopathNo)
and
Naturopath (NaturopathNo, name, contactNo, officeStartTime, officeEndTime, emailAddress)
now to query this i've come up with
SELECT count(*), naturopathno FROM dbf10.patient WHERE naturopathno != 'NULL' GROUP BY naturopathno;
which results in;
COUNT(*) NATUROPATH
2 NP5
1 NP6
3 NP2
1 NP1
2 NP3
1 NP7
2 NP8
My question is, how would I go about selecting the highest count from this list, and printing that value with the naturopaths name? Any suggestions are very welcome,
In MySQL, you could select the top row like:
select *
from Naturopath n
join (
SELECT count(*) as cnt, naturopathno
FROM dbf10.patient
WHERE naturopathno != 'NULL'
GROUP BY naturopathno;
) pat ON pat.naturopathno = n.naturopathno
ORDER BY pat.cnt DESC
LIMIT 1
By the way, if you're checking for null instead of the string "NULL", try:
where naturopathno is not null
You can use the RANK analytic function - this will assign rank "1" to the topmost naturopath, or naturopaths if there is a tie for first place:
SELECT (select name from Naturopath n
where n.NaturopathNo = q.naturopathno)
as TopNaturopathName,
,q.patients
FROM (
SELECT naturopathno, patients,
RANK() OVER (ORDER BY patients DESC) rnk
FROM (
SELECT COUNT(*) AS patients, naturopathno
FROM dbf10.patient
WHERE naturopathno is not null
GROUP BY naturopathno
)
) q
WHERE rnk = 1;
okay figured it out, thanks guys, ive got this which does the job, probably not very efficiently but does do it :)
SELECT *
FROM (
SELECT COUNT(*) AS patients, naturopathno
FROM dbf10.patient
WHERE naturopathno is not null
GROUP BY naturopathno
ORDER BY patients DESC)
WHERE ROWNUM = 1;
any better ways to do this?

Resources