Semi-Tricky SQL Query - sql

I am trying to write a query for SQL Server 2005 but I can't figure out how to do it. I have a table with the following fields:
MessageID int
CategoryID int
Priority tinyint
MessageText NVARCHAR(MAX)
I need a query that will return * for each row that has the highest priority within a Category. For example, if I had the following data:
MessageID, CategoryID, Priority, MessageText
1, 100, 1, Error #1234 occurred
2, 100, 2, Error #243 occurred
3, 100, 3, Error #976 occurred
4, 200, 4, Error #194 occurred
5, 200, 1, Error #736 occurred
6, 300, 3, Error #54 occurred
7, 300, 2, Error #888 occurred
then the result would be:
MessageID, CategoryID, Priority, MessageText
3, 100, 3, Error #976 occurred
4, 200, 4, Error #194 occurred
6, 300, 3, Error #54 occurred
Notice that it returns one row per category, and that it is the row which had the highest priority for that Category.
Can anyone tell me how I can write this query?

Verified:
SELECT
highest_priority_messages.*
FROM
(
SELECT
m.MessageID
, m.CategoryID
, m.Priority
, m.MessageText
, Rank() OVER
(PARTITION BY m.CategoryID ORDER BY m.Priority DESC) AS p_rank
FROM [Message] m
GROUP BY
m.CategoryID
, m.Priority
, m.MessageID
, m.MessageText
) highest_priority_messages
WHERE
p_rank = 1

I believe that this should work, table name assumed as Messages
SELECT
M.MessageId,
M.CategoryId,
M.Priority,
M.MessageText
FROM
(
SELECT
CategoryId,
MAX(Priority) AS Priority
FROM Messages
GROUP BY CategoryId
) AS MaxValues
INNER JOIN Messages M
ON (MaxValues.CategoryId = M.CategoryId
AND MaxValues.Priority = M.Priority)
NOTE
The only "gotcha" in this method is that if you have more than one max priority...

If you'd like to do it without all of the subqueries:
SELECT
MessageID,
CategoryID,
Priority,
MessageText
FROM
dbo.Messages M1
LEFT OUTER JOIN dbo.Messages M2 ON
M2.CategoryID = M1.CategoryID AND
M2.Priority > M1.Priority
WHERE
M2.MessageID IS NULL
You might have to adjust the query depending on how you want to handle ties. You didn't have any such examples, so I wasn't sure.

select distinct query1.* from
(select categoryId,msgText,max(priorityId) as MAX_PRIORITY
from message
group by categoryId,msgText
order by categoryId
) query1,
(select categoryId,max(priorityId) as MAX_PRIORITY
from message
group by categoryId
order by categoryId
) query2
where query1.MAX_PRIORITY = query2.MAX_PRIORITY
order by query1.categoryId

SELECT
Messages.MessageID
, Messages.CategoryID
, Messages.Priority
, Messages. MessageText
FROM
Messages
INNER JOIN
(
SELECT
CategoryID
, MAX(Priority) AS Priority
FROM
Messages
GROUP BY
CategoryID
) AS MaxResults
ON
(
Messages.CategoryID = MaxResults.CategoryID
AND
Messages.Priority = MaxResults.Priority
)
It looks like this is basically the same answer given above... with the same Caveat.
Although this one will work right off the bat.

This is shorter and easier to read (imo).
select ms.*
from
messages ms
,(
select ms1.categoryid, max(ms1.priority) as max_priority
from messages ms1
group by ms1.categoryid
) tmp
where ms.categoryid = tmp.categoryid
and ms.priority = tmp.max_priority;

SELECT DISTINCT CategoryId,PS.Priority,MessageID,MessageText
FROM Priority_Scene PS
JOIN (SELECT MAX(Priority) AS Priority FROM Priority_Scene GROUP BY CategoryId) A
ON A.Priority = PS.Priority

I'm not quite high enough rank (yet) to post a comment, so I'd like to add to cfeduke's solution:
SELECT
highest_priority_messages.*
FROM
(
SELECT
m.MessageID
, m.CategoryID
, m.Priority
, m.MessageText
, Rank() OVER
(PARTITION BY m.CategoryID ORDER BY m.Priority DESC, m.MessageID DESC) AS p_rank
FROM [Message] m
GROUP BY
m.CategoryID
, m.Priority
, m.MessageID
, m.MessageText
) highest_priority_messages
WHERE
p_rank = 1
If you add another CategoryID 100 Message with Priority 3, the original solution would bring back 2 rows, by adding another order condition we eliminate the chance of two items ranking the same.
Here's a copy of the row I inserted to test this.
insert into [Message] (MessageID, CategoryID, Priority, MessageText)
select 8, 100, 3, 'Error #976-2 occurred'

Related

Insert occasionally missing rows

I am running below query, sometimes these query skip few records to insert into newordersholdentry and didn't get any error.
but if run same query again after finding it is missed some records for order, it will insert all.
Please let me know what could be the reason.
INSERT INTO newordersholdentry
(itemid,
lookupcode,
description,
gamacode,
ordered,
IsForceItem,
Scanned,
Location,
SortOrder
)
SELECT ID,
ItemLookupCode,
Description,
GamaCode,
SUM(Qty) AS Qty,
ForceItem,
0 AS Scanned,
SubDescription1,
Sortorder
FROM
(
SELECT Item.ID,
Item.ItemLookupCode,
Item.Description,
NewOrderItems.GamaCode,
NewOrderItems.Qty,
Item.SubDescription1,
Item.Binlocation,
ISNULL(
(
SELECT TOP (1) SortSno
FROM NewOrderPickPackSorting
WHERE(Bin = LEFT(Item.SubDescription1, 3))
), 99999) AS Expr1,
0 AS ForceItem,
p.sortorder
FROM NewOrderItems(NOLOCK)
INNER JOIN Item(NOLOCK) ON NewOrderItems.GamaCode = Item.SubDescription2
LEFT OUTER JOIN pickpath(NOLOCK) p ON concat(RTRIM(p.aisle), '-', p.section) = UPPER(LEFT(item.subdescription1, 6))
WHERE(NewOrderItems.Discontinue = 0)
AND (NewOrderItems.OrderID = 123456)
) AS t
Group by ID
, ItemLookupcode
, Description
, GAMACODE
, ForceItem
, Expr1
, BinLocation
, SubDescription1
, sortorder

Get only best ranked rows from a subquery

I want to get the price of an article for a specific customer.
There are several levels of prices which i ranked in my query.
So Article A has a price on rank 1, 4, 6. The result should always be the lowest ranked price.
Article B rank 3 ,5
So article A price is ranked 1 and Article b is price ranked 3.
My query is below .
SELECT p2.* FROM(
SElect ART_ID, MIN(RANG) RANG FROM (
Select p.ART_ID, p.betrag ,
CASE p.PREIS_EBENE WHEN 'KA' THEN 1 WHEN 'KW' THEN 2 WHEN 'W' THEN 7 WHEN 'A' THEN 6 ELSE 99 END RANG
FROM MDART a
INNER JOIN MDPRSVK p ON (a.KLIENT_ID = p.KLIENT_ID AND a.ART_ID = p.ART_ID)
WHERE ICP_KZ.IS_SET(KENNUNG_USER, 'P') = 1
ORDER BY RANG)
GROUP BY ART_ID) T
INNER JOIN MDPRSVK p2 ON (p2.ART_ID = T.ART_ID AND p2.PREIS_EBENE = p.PREIS_EBENE)
i want to have every article appearing only once in the result
You have tagged your request PL/SQL, so I guess your DBMS may be Oracle.
If I understand correctly, the table MDPRSVK contains several prices per ART_ID. And you want to select each ART_ID's best price (best to worst: 'KA' -> 'KW' -> 'A' -> 'W' -> any other PREIS_EBENE).
You can use a window function (ROW_NUMBER, RANK or DENSE_RANK) for this:
select *
from mdprsvk
order by row_number()
over (partition by art_id
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
fetch first row with ties;
This is standard SQL. In Oracle, FETCH FIRST is available as of version 12c. In earlier versions you'd use a subquery instead:
select *
from
(
select
mdprsvk.*,
row_number() over (partition by art_id
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
as rn
from mdprsvk
)
where rn = 1;
Or use OraclesKEEP FIRST`:
select art_id, max(betrag)
keep (dense_rank first
order by decode(preis_ebene, 'KA', 1, 'KW', 2, 'A', 3, 'W', 4, 5))
from mdprsvk
group by art_id;
It is not clear, how MDART comes into play. It looks like you want to restrict your results to articles for certain clients and KENNUNG_USER is the column in MDART to check. If so, add a WHERE clause:
where exists
(
select *
from mdart
where mdart.klient_id = mdprsvk.klient_id
and mdart.art_id = mdprsvk.art_id
and icp_kz.is_set(mdart.kennung_user, 'p') = 1
)
Or with IN instead of EXISTS:
where (klient_id, art_id) in
(
select klient_id, art_id
from mdart
where icp_kz.is_set(kennung_user, 'p') = 1
)

Where clause on Running total

I have this table which stores containers by region and the number of coffee pouches in each of the containers.
if object_id( 'dbo.Container' ) is not null
drop table dbo.Container
go
create table dbo.Container
(
Id int not null,
Region int not null,
NumberOfCoffeePouches int not null,
constraint pkc_Container__Id primary key clustered(Id asc)
)
go
insert into dbo.Container
( Id , Region , NumberOfCoffeePouches )
values
( 1, 1, 10 ),
( 2, 1, 30 ),
( 3, 1, 5),
( 4, 1, 7),
( 5, 1, 1),
( 6, 1, 3),
( 7, 2, 4),
( 8, 2, 4),
( 9, 2, 4)
I need to list out the container Ids that will be used to fulfill an order of, say 50, coffee pouches. Over supplying is OK.
Here is query I have come up with
declare #RequiredCoffeePouches int = 50
select
sq2.Id,
sq2.NumberOfCoffeePouches,
sq2.RunningTotal,
sq2.LagRunningTotal
from
(
select
sq1.Id,
sq1.NumberOfCoffeePouches,
sq1.RunningTotal,
lag(sq1.RunningTotal, 1, 0) over (order by sq1.Id asc)
as 'LagRunningTotal'
from
(
select
c.Id,
c.NumberOfCoffeePouches,
sum(c.NumberOfCoffeePouches)
over (order by c.Id asc) as 'RunningTotal'
from
dbo.Container as c
where
c.Region = 1
) as sq1
) as sq2
where
sq2.LagRunningTotal <= #RequiredCoffeePouches
It gives the expected result
Id NumberOfCoffeePouches RunningTotal LagRunningTotal
----------- --------------------- ------------ ---------------
1 10 10 0
2 30 40 10
3 5 45 40
4 7 52 45
Question:
Is there a better and more optimized way to achieve this?
Specially the Container table is very large table and I think the sub query sq1 will unnecessarily calculate the RunningTotals for all the containers in the region. I was wondering if there is anyway to have sq1 stop processing more rows once the RunnningTotal exceeds over the #RequiredCoffeePouches.
Two things:
Moving your WHERE clause inside of the relevant sub-select can greatly increase the speed of the query because it'll pull less data. Using your example:
SELECT
sq2.Id,
sq2.NumberOfCoffeePouches,
sq2.RunningTotal,
sq2.LagRunningTotal
FROM
(
SELECT
sq1.Id,
sq1.NumberOfCoffeePouches,
sq1.RunningTotal,
lag(sq1.RunningTotal, 1, 0) over (order by sq1.Id asc) AS 'LagRunningTotal'
FROM
(
SELECT
c.Id,
c.NumberOfCoffeePouches,
SUM(c.NumberOfCoffeePouches) OVER (order by c.Id asc) AS 'RunningTotal'
FROM dbo.Container AS c
WHERE c.Region = 1
) AS sq1
WHERE sq2.LagRunningTotal <= #RequiredCoffeePouches
) AS sq2
CTEs can also improve performance:
;WITH sql1CTE AS (
SELECT
c.Id,
c.NumberOfCoffeePouches,
SUM(c.NumberOfCoffeePouches) OVER (order by c.Id asc) AS 'RunningTotal'
FROM dbo.Container AS c
WHERE c.Region = 1
),
sql2CTE AS (
SELECT
Id,
NumberOfCoffeePouches,
RunningTotal,
lag(RunningTotal, 1, 0) over (order by Id asc) AS 'LagRunningTotal'
FROM sql1CTE
WHERE LagRunningTotal <= #RequiredCoffeePouches
)
SELECT
Id,
NumberOfCoffeePouches,
RunningTotal,
LagRunningTotal
FROM sql2CTE
SQL Server CTE Basics
If you're using SSMS, select "Include Client Statistics" and "Include Actual Execution Plan" to keep track of how your query performs while you're crafting it.

Most recent rows with column from previous most recent rows

I have fetched all the rows with most recent rows for every orderID with this VIEW.But I also need another Column "PreviousStatusID" which will store the previous most recent row's StatusID.. Is it possible to implement ? Please Help.
The View :
ALTER VIEW [dbo].[VW_PatientOrderByMaxTimeAddedProc]
AS
SELECT
t.ID,
t.ExamDate,
t.ArrivalTime,
t.Activity,
t.PatientFirstName,
t.PatientMiddleName,
t.PatientLastName,
t.DOB,
t.[Order],
t.ActualExamTimeIn,
t.ActualExamTimeOut,
t.ActualScannerID,
t.ActualExamDate,
t.ActualCustomer,
t.ActualPatientFirstName,
t.ActualPatientLastName,
t.ActualDOB,
t.InsuranceCoID,
t.InsuranceID,
t.StartedInPreAuth,
t.DateReceived,
t.TimeReceived,
FDGPatientOrder,
StatusID,
TimeAdded ,
Notes,
cntID,
empID,
Isotope,
Weight,
Diabetic,
Indication,
[Procedure],
InjectionTime,
PhysicianFirstName ,
PhysicianText,
IndicationDescription
FROM
dbo.smsFDGPatientOrder t
INNER JOIN
dbo.smsFDGPatientOrderStatus ON t.ID = FDGPatientOrder
INNER JOIN
(
SELECT
smsFDGPatientOrder.ID,
MAX(TimeAdded) AS MAX_TIME
FROM
dbo.smsFDGPatientOrder AS smsFDGPatientOrder
INNER JOIN
dbo.smsFDGPatientOrderStatus ON smsFDGPatientOrder.ID = FDGPatientOrder
GROUP BY
smsFDGPatientOrder.ID
)
a ON a.ID = t.ID and a.MAX_TIME = TimeAdded
What version of SQL? In 2012+, you can use the LAG window function to make this not too bad.
SELECT WindowedView.FDGPatientOrder
FROM (
SELECT FDGPatientOrder,
StatusId,
TimeAdded,
MAX(TimeAdded) OVER (PARTITION BY FDGPatientOrder) AS MostRecentTimeAdded,
LAG(StatusId, 1, NULL) OVER (PARTITION BY FDGPatientOrder ORDER BY TimeAdded DESC) AS PrevStatusId
FROM PatientOrders
) WindowedView
WHERE WindowedView.StatusId = 3
AND WindowedView.PrevStatusId IN (4, 9, 10, 20, 21, 22)
AND WindowedView.TimeAdded = WindowedView.MostRecentTimeAdded

Get count of posts from a union mysql query?

SELECT u.id AS pid ,
b2.id AS id ,
b2.message AS MESSAGE,
b2.uid AS uid,
b2.date AS DATE
FROM (
(SELECT b.id AS id ,
b.pid AS pid ,
b.message AS MESSAGE,
b.uid AS uid,
b.date AS DATE
FROM wall_posts AS b
JOIN Friends AS f
ON f.id = b.pid
WHERE f.buddy_id = '1'
AND f.status = 'b'
ORDER BY DATE DESC
LIMIT 0, 10
)
UNION
(SELECT id ,
pid ,
MESSAGE,
uid,
DATE
FROM wall_posts
WHERE pid = '1'
ORDER BY DATE DESC
LIMIT 0, 10
)
) AS b2
JOIN Users AS u
ON b2.pid = u.id
WHERE u.banned ='0'
AND u.email_activated='1'
ORDER BY DATE DESC
LIMIT 0, 10
Is the code. Not sure how I would get the post count with this. I know normally i would do select count(*) as num I tried
So what i did i took
SELECT u.id AS pid ,
b2.id AS id ,
b2.message AS MESSAGE,
b2.uid AS uid,
b2.date AS DATE
FROM
and changed it to
SELECT COUNT(u.id AS pid ,
b2.id AS id ,
b2.message AS MESSAGE,
b2.uid AS uid,
b2.date AS DATE) as num
FROM
and did something similar for all the the select statements, well that didn't work, kept getting errors like #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS pid , b2.id AS id , b2.message AS MESSAGE, b' at line 1. So how would i go about getting the count? I need the count for my pagination php class.
The COUNT function doesn't allow the multiple columns you have. It can be COUNT(*) or COUNT(column_name), where there is a single column name. The COUNT(*) format counts total number of rows, where the COUNT(column_name) returns count of the non-null values for the the specified column.
So, the next step is to alter your COUNT in the SELECT. Then, you can go from there if other issues exist.
mysql_num_rows in php seems to pull it off without modifying the sql.