Complex 'order by' or maybe sorting issue - sql

I have a table that looks like this
Group Recipe Priority
0 A 400
0 A 200
0 B 500
0 B 100
1 C 300
1 C 300
1 D 600
Importance is "Group" > "Priority" > "Recipe"
Group 0 has to go first.
Within Group 0, Priority 500 has to go first (since it has higher priority), but for the efficiency, all the recipe has to go first.
After sorting,
it should look like this
Group Recipe Priority
0 B 500
0 B 100
0 A 400
0 A 200
1 D 600
1 C 300
1 C 300
I have tried all different ways to do 'order by' but cannot find a correct way.
thank you for the help

The problem is subtle. You want to order not by priority, but by the maximum priority for a given group/recipe combination. That is, you want to keep all the recipes together based on this max priority.
The following does this:
select t.Group, t.Recipe, t.Priority,
max(priority) over (partition by t.group, t.recipe) as maxpriority
from tablename t
order by t.Group asc, 4 desc, t.Recipe, priority desc

In older versions of Oracle, prior to having analytic functions available, we would have returned the specified result set using a query something like this:
SELECT f.group
, f.recipe
, f.priority
FROM foo f
JOIN ( SELECT g.group
, g.recipe
, MAX(g.priority) AS max_priority
FROM foo g
GROUP BY g.group, g.recipe
) m
ON m.group = f.group AND m.recipe = f.recipe
ORDER BY f.group
, m.max_priority DESC
, f.recipe
, f.priority DESC
This approach works in other databases that don't have analytic functions, such as MySQL.
NOTE: The query above is not NULL-safe, in that the JOIN predicates will eliminate rows that have NULL values for the group or recipe columns. It could be made NULL-safe, but it complicates the SQL a bit.
SELECT f.group
, f.recipe
, f.priority
FROM foo f
JOIN ( SELECT g.group
, g.recipe
, MAX(g.priority) AS max_priority
FROM foo g
GROUP BY g.group, g.recipe
) m
ON (m.group = f.group OR COALESCE(m.group,f.group) IS NULL)
AND (m.recipe = f.recipe OR COALESCE(m.recipe,f.recipe) IS NULL)
ORDER BY f.group
, m.max_priority DESC
, f.recipe
, f.priority DESC
An equivalent result can also be obtained using a correlated subquery in the SELECT list, except that this result set contains an extra "max_priority" column in the result set.
SELECT f.group
, f.recipe
, f.priority
, (SELECT MAX(g.priority)
FROM foo g
WHERE (g.group = f.group OR COALESCE(g.group,f.group) IS NULL)
AND (g.recipe = f.recipe OR COALESCE(g.recipe,f.recipe) IS NULL)
) AS max_priority
FROM foo f
ORDER BY f.group
, 4 DESC
, f.recipe
, f.priority DESC
(I haven't tested whether that correlated subquery could be removed from the SELECT list and entirely moved to the ORDER BY clause. If that worked, we'd eliminate returning the extra column, but that query would look really, really odd.) The other option (to omit that extra column) is to wrap this query (as an inline view) in another query.
SELECT e.group
, e.recipe
, e.priority
FROM (
SELECT f.group
, f.recipe
, f.priority
, (SELECT MAX(g.priority)
FROM foo g
WHERE (g.group = f.group OR COALESCE(g.group,f.group) IS NULL)
AND (g.recipe = f.recipe OR COALESCE(g.recipe,f.recipe) IS NULL)
) AS max_priority
FROM foo f
) e
ORDER BY e.group
, e.max_priority DESC
, e.recipe
, e.priority DESC

Related

Use of MAX function in SQL query to filter data

The code below joins two tables and I need to extract only the latest date per account, though it holds multiple accounts and history records. I wanted to use the MAX function, but not sure how to incorporate it for this case. I am using My SQL server.
Appreciate any help !
select
PROP.FileName,PROP.InsName, PROP.Status,
PROP.FileTime, PROP.SubmissionNo, PROP.PolNo,
PROP.EffDate,PROP.ExpDate, PROP.Region,
PROP.Underwriter, PROP_DATA.Data , PROP_DATA.Label
from
Property.dbo.PROP
inner join
Property.dbo.PROP_DATA on Property.dbo.PROP.FileID = Actuarial.dbo.PROP_DATA.FileID
where
(PROP_DATA.Label in ('Occupancy' , 'OccupancyTIV'))
and (PROP.EffDate >= '42278' and PROP.EffDate <= '42643')
and (PROP.Status = 'Bound')
and (Prop.FileTime = Max(Prop.FileTime))
order by
PROP.EffDate DESC
Assuming your DBMS supports windowing functions and the with clause, a max windowing function would work:
with all_data as (
select
PROP.FileName,PROP.InsName, PROP.Status,
PROP.FileTime, PROP.SubmissionNo, PROP.PolNo,
PROP.EffDate,PROP.ExpDate, PROP.Region,
PROP.Underwriter, PROP_DATA.Data , PROP_DATA.Label,
max (PROP.EffDate) over (partition by PROP.PolNo) as max_date
from Actuarial.dbo.PROP
inner join Actuarial.dbo.PROP_DATA
on Actuarial.dbo.PROP.FileID = Actuarial.dbo.PROP_DATA.FileID
where (PROP_DATA.Label in ('Occupancy' , 'OccupancyTIV'))
and (PROP.EffDate >= '42278' and PROP.EffDate <= '42643')
and (PROP.Status = 'Bound')
and (Prop.FileTime = Max(Prop.FileTime))
)
select
FileName, InsName, Status, FileTime, SubmissionNo,
PolNo, EffDate, ExpDate, Region, UnderWriter, Data, Label
from all_data
where EffDate = max_date
ORDER BY EffDate DESC
This also presupposes than any given account would not have two records on the same EffDate. If that's the case, and there is no other objective means to determine the latest account, you could also use row_numer to pick a somewhat arbitrary record in the case of a tie.
Using straight SQL, you can use a self-join in a subquery in your where clause to eliminate values smaller than the max, or smaller than the top n largest, and so on. Just set the number in <= 1 to the number of top values you want per group.
Something like the following might do the trick, for example:
select
p.FileName
, p.InsName
, p.Status
, p.FileTime
, p.SubmissionNo
, p.PolNo
, p.EffDate
, p.ExpDate
, p.Region
, p.Underwriter
, pd.Data
, pd.Label
from Actuarial.dbo.PROP p
inner join Actuarial.dbo.PROP_DATA pd
on p.FileID = pd.FileID
where (
select count(*)
from Actuarial.dbo.PROP p2
where p2.FileID = p.FileID
and p2.EffDate <= p.EffDate
) <= 1
and (
pd.Label in ('Occupancy' , 'OccupancyTIV')
and p.Status = 'Bound'
)
ORDER BY p.EffDate DESC
Have a look at this stackoverflow question for a full working example.
Not tested
with temp1 as
(
select foo
from bar
whre xy = MAX(xy)
)
select PROP.FileName,PROP.InsName, PROP.Status,
PROP.FileTime, PROP.SubmissionNo, PROP.PolNo,
PROP.EffDate,PROP.ExpDate, PROP.Region,
PROP.Underwriter, PROP_DATA.Data , PROP_DATA.Label
from Actuarial.dbo.PROP
inner join temp1 t
on Actuarial.dbo.PROP.FileID = t.dbo.PROP_DATA.FileID
ORDER BY PROP.EffDate DESC

PL/SQL-How to implement multiple count statements in a single query when tables and conditions are all different

I'm working on Open Text Content Server Tool that uses PL/SQL Database. What I am trying to do is to fetch count data through 6 different queries all having different conditions and different tables too. I was trying to combine all these 6 count queries but no luck. Below are those 6 queries listed :
Documents Created in A Month:
select count (dataid) from Dtree where
Createdate >= %1 and createdate <= %2 and subtype = 144
Total No of Users:
select count(a.id) from Kuaf a, kuaf b where
a.deleted =0 and a.type =0 and b.id = a.groupid
Unique Users Logged in a Month(Count):
Select count (distinct (performerID))
from dauditnew where auditid=23 and auditdate >= %1 and auditdate <= %2
Users Created in a Month(Count):
Select Count(dataid) FROM DAUDITNEW where AUDITID = 1
AND AUDITSTR LIKE 'Create' and subtype=142 AND
auditdate >= %1 and auditdate <= %2
Users Deleted(Count):
SELECT count(a.userid) from dauditnew a WHERE
a.auditstr = 'Delete' AND
a.AuditDate >= %1 AND
a.AuditDate <= %2 AND
a.UserID in (Select ID from KUAF where Deleted = 1 and Type=0)
Workflows Initiated:
Select count(*) from Wworkaudit WWA where WWA.workaudit_status=1 AND
WWA.workaudit_date >= %1 and WWA.workaudit_date <= %2
Here %1,%2 denote user inputs. Since these 6 queries all have very different conditions, it seems a daunting task for me to combine them. Please help me out.
Thank You.
SELECT (
select count (dataid)
from Dtree
where Createdate BETWEEN :start_date and :end_date
and subtype = 144
) AS Docs_Per_Month,
(
select count(a.id)
from Kuaf a INNER JOIN kuaf b ON (b.id = a.groupid)
where a.deleted = 0
and a.type = 0
) AS Total_No_of_Users,
(
Select count( distinct performerID )
from dauditnew
where auditid = 23
and auditdate BETWEEN :start_date and :end_date
) AS Unique_Users_in_Month,
(
Select Count(dataid)
FROM DAUDITNEW
where AUDITID = 1
AND AUDITSTR = 'Create'
and subtype = 142
AND auditdate BETWEEN :start_date and :end_date
) AS Users_Created_in_Month,
(
SELECT count(a.userid)
from dauditnew a
WHERE a.auditstr = 'Delete'
AND a.auditdate BETWEEN :start_date and :end_date
AND a.UserID in (Select ID from KUAF where Deleted = 1 and Type=0)
) AS Users_Deleted,
(
Select count(*)
from Wworkaudit
where workaudit_status = 1
AND workaudit_date BETWEEN :start_date and :end_date
) AS Workflows_Initiated
FROM DUAL;
Use UNION ALL statement
Ex.
select count (a.x) from a...where...
UNION ALL
select count (b.z) from b...where...
UNION ALL
select count (c.y) from c...where...
etc.
Note: you must use UNION ALL, because if you use regular UNION, duplicate results will not be shown

PostgreSQL use case when result in where clause

I use complex CASE WHEN for selecting values. I would like to use this result in WHERE clause, but Postgres says column 'd' does not exists.
SELECT id, name, case when complex_with_subqueries_and_multiple_when END AS d
FROM table t WHERE d IS NOT NULL
LIMIT 100, OFFSET 100;
Then I thought I can use it like this:
select * from (
SELECT id, name, case when complex_with_subqueries_and_multiple_when END AS d
FROM table t
LIMIT 100, OFFSET 100) t
WHERE d IS NOT NULL;
But now I am not getting a 100 rows as result. Probably (I am not sure) I could use LIMIT and OFFSET outside select case statement (where WHERE statement is), but I think (I am not sure why) this would be a performance hit.
Case returns array or null. What is the best/fastest way to exclude some rows if result of case statement is null? I need 100 rows (or less if not exists - of course). I am using Postgres 9.4.
Edited:
SELECT count(*) OVER() AS count, t.id, t.size, t.price, t.location, t.user_id, p.city, t.price_type, ht.value as houses_type_value, ST_X(t.coordinates) as x, ST_Y(t.coordinates) AS y,
CASE WHEN t.classification='public' THEN
ARRAY[(SELECT i.filename FROM table_images i WHERE i.table_id=t.id ORDER BY i.weight ASC LIMIT 1), t.description]
WHEN t.classification='protected' THEN
ARRAY[(SELECT i.filename FROM table_images i WHERE i.table_id=t.id ORDER BY i.weight ASC LIMIT 1), t.description]
WHEN t.id IN (SELECT rl.table_id FROM table_private_list rl WHERE rl.owner_id=t.user_id AND rl.user_id=41026) THEN
ARRAY[(SELECT i.filename FROM table_images i WHERE i.table_id=t.id ORDER BY i.weight ASC LIMIT 1), t.description]
ELSE null
END AS main_image_description
FROM table t LEFT JOIN table_modes m ON m.id = t.mode_id
LEFT JOIN table_types y ON y.id = t.type_id
LEFT JOIN post_codes p ON p.id = t.post_code_id
LEFT JOIN table_houses_types ht on ht.id = t.houses_type_id
WHERE datetime_sold IS NULL AND datetime_deleted IS NULL AND t.published=true AND coordinates IS NOT NULL AND coordinates && ST_MakeEnvelope(17.831490030182, 44.404640972306, 12.151558389557, 47.837396630872) AND main_image_description IS NOT NULL
GROUP BY t.id, m.value, y.value, p.city, ht.value ORDER BY t.id LIMIT 100 OFFSET 0
To use the CASE WHEN result in the WHERE clause you need to wrap it up in a subquery like you did, or in a view.
SELECT * FROM (
SELECT id, name, CASE
WHEN name = 'foo' THEN true
WHEN name = 'bar' THEN false
ELSE NULL
END AS c
FROM case_in_where
) t WHERE c IS NOT NULL
With a table containing 1, 'foo', 2, 'bar', 3, 'baz' this will return records 1 & 2. I don't know how long this SQL Fiddle will persist, but here is an example: http://sqlfiddle.com/#!15/1d3b4/3 . Also see https://stackoverflow.com/a/7950920/101151
Your limit is returning less than 100 rows if those 100 rows starting at offset 100 contain records for which d evaluates to NULL. I don't know how to limit the subselect without including your limiting logic (your case statements) re-written to work inside the where clause.
WHERE ... AND (
t.classification='public' OR t.classification='protected'
OR t.id IN (SELECT rl.table_id ... rl.user_id=41026))
The way you write it will be different and it may be annoying to keep the CASE logic in sync with the WHERE limiting statements, but it would allow your limits to work only on matching data.

Incorrect data when run as subquery

I have a query that takes data about given media from a table it joins it with the user table
:
SELECT media.id, media.user_id,#rownum := #rownum + 1 AS position
INNER JOIN users
ON media.user_id = users.id
FROM media_table
ORDER BY media.distance ASC, media.media_likes_count DESC, media.media_views_count Desc;
This query produces a nice looking table as follows:
media_id, user_id, position
39199 , 3949 , 1
39299 , 3149 , 2
39359 , 3944 , 3
39369 , 3349 , 4
39379 , 3149 , 5
39389 , 3449 , 6
From this derived table, I want to get position of media_id = 39389.
However if I include that query in subquery like this:
Select position from (SELECT media.id, media.user_id,#rownum := #rownum + 1 AS position from
INNER JOIN users
ON media.user_id = users.id
FROM media_table
ORDER BY media.distance ASC, media.media_likes_count DESC, media.media_views_count Desc;)
where media_id = 39389
Then the columns 'shuffle' and 39389 does not have position 6 anymore.
Not withstanding the errors in your query, assuming those are just typos, perhaps you're issue is not initializing your user defined variable. This condensed version works for me:
select postition
from (
select yourresults.*, #rn:=#rn+1 postition
from yourresults
join (select #rn:= 0) t
order by media_id
) t
where media_id = 39389
SQL Fiddle Demo
While this does not work:
select postition
from (
select yourresults.*, #rn:=#rn+1 postition
from yourresults
order by media_id
) t
where media_id = 39389
More Fiddle

Fastest way to check if the the most recent result for a patient has a certain value

Mssql < 2005
I have a complex database with lots of tables, but for now only the patient table and the measurements table matter.
What I need is the number of patient where the most recent value of 'code' matches a certain value. Also, datemeasurement has to be after '2012-04-01'. I have fixed this in two different ways:
SELECT
COUNT(P.patid)
FROM T_Patients P
WHERE P.patid IN (SELECT patid
FROM T_Measurements M WHERE (M.code ='xxxx' AND result= 'xx')
AND datemeasurement =
(SELECT MAX(datemeasurement) FROM T_Measurements
WHERE datemeasurement > '2012-01-04' AND patid = M.patid
GROUP BY patid
GROUP by patid)
AND:
SELECT
COUNT(P.patid)
FROM T_Patient P
WHERE 1 = (SELECT TOP 1 case when result = 'xx' then 1 else 0 end
FROM T_Measurements M
WHERE (M.code ='xxxx') AND datemeasurement > '2012-01-04' AND patid = P.patid
ORDER by datemeasurement DESC
)
This works just fine, but it makes the query incredibly slow because it has to join the outer table on the subquery (if you know what I mean). The query takes 10 seconds without the most recent check, and 3 minutes with the most recent check.
I'm pretty sure this can be done a lot more efficient, so please enlighten me if you will :).
I tried implementing HAVING datemeasurment=MAX(datemeasurement) but that keeps throwing errors at me.
So my approach would be to write a query just getting all the last patient results since 01-04-2012, and then filtering that for your codes and results. So something like
select
count(1)
from
T_Measurements M
inner join (
SELECT PATID, MAX(datemeasurement) as lastMeasuredDate from
T_Measurements M
where datemeasurement > '01-04-2012'
group by patID
) lastMeasurements
on lastMeasurements.lastmeasuredDate = M.datemeasurement
and lastMeasurements.PatID = M.PatID
where
M.Code = 'Xxxx' and M.result = 'XX'
The fastest way may be to use row_number():
SELECT COUNT(m.patid)
from (select m.*,
ROW_NUMBER() over (partition by patid order by datemeasurement desc) as seqnum
FROM T_Measurements m
where datemeasurement > '2012-01-04'
) m
where seqnum = 1 and code = 'XXX' and result = 'xx'
Row_number() enumerates the records for each patient, so the most recent gets a value of 1. The result is just a selection.