Discriminate data from functions aggregate on PosgreSQL - sql

On PosgreSQL I have a database of pizza restaurant.
With this code:
SELECT command.id_command, array_agg(history_state_of_command.state)
FROM command JOIN history_state_of_command
ON command.id_command = history_state_of_command.id_command
GROUP BY command.id_command
I obtain these results, with the id of a command and the associated state of command:
command.id_command State of command
1
"{Pizza_Order,Pizza_in_preparation,Pizza_prepared,Pizza_ready_for_delivery,Pizza_delivering,Pizza_deliver}"
2
"{Pizza_Order,Pizza_in_preparation}"
3
"{Pizza_Order,Pizza_in_preparation,Pizza_prepared,Pizza_ready_for_delivery,Pizza_delivering,Pizza_deliver,"Command cancelled"}"
4
"{Pizza_Order,Pizza_in_preparation,Pizza_prepared,Pizza_ready_for_delivery,Pizza_delivering,Pizza_deliver}"
I would like to find an SQL code where I obtain only id of command where the pizza was never prepared:
command.id_command State of command
2 "{Pizza_Order,Pizza_in_preparation}"
Many thanks for your help !

You can use a correlated subquery to find this command:
select h.id_command
from history_state_of_command h
where h.state in ('Pizza_Order', 'Pizza_in_preparation')
and not exists (
select 1
from history_state_of_command i
where i.id_command = h.id_command and i.state = 'Pizza_prepared'
)

You can use aggregation as well:
select hsc.id_command
from history_state_of_command hsc
group by hsc.id_command
having count(*) filter (where hsc.state = 'Pizza_prepared') = 0;
Note: This assumes that commands have some row in the history. If not, then use not exists;
select c.*
from command c
where not exists (select 1
from history_state_of_command hsc
where hsc.id_command = c.id_command and hsc.state = 'Pizza_prepared'
);
This is probably the most efficient method, with appropriate indexes.

Related

How do I convert a set of basic (non-nested) views into a single nested query?

I have used a sequence of basic non-nested SQL View queries to slowly filter data from multiple tables to obtain a result I was after. The first query has used base relations as the input, the subsequent queries have used the base relations and/or the results from the previous query. The final query has displayed the result I was after.
First query:
CREATE VIEW ActiveRobbers
AS
SELECT a.RobberID, Nickname, NoYears, BankName, Share
FROM info.Robber a, info.Accomplices b
WHERE a.RobberID = b.RobberID;
Second query:
CREATE VIEW ActiveRobbers2
AS
SELECT Nickname, NoYears, COUNT(RobberID) AS NoRobberies, SUM(Share) AS TotalEarnings
FROM ActiveRobbers
GROUP BY Nickname, NoYears;
Final query:
CREATE VIEW ActiveRobbers3
AS
SELECT Nickname
FROM ActiveRobbers2
WHERE NoYears = 0
AND NoRobberies > (SELECT AVG(NoRobberies) FROM ActiveRobbers2);
How would I go about converting this into a single nested query using select statements? Is there some sort of rule you can use? Do I work backwards because of the nesting?
If you are learning SQL, learn to use correct and explicit join syntax. So your first query would be:
SELECT r.RobberID, a.Nickname, a.NoYears, a.BankName, a.Share
FROM info.Robber r JOIN
info.Accomplices a
on r.RobberID = a.RobberID;
The second:
SELECT a.Nickname, a.NoYears, COUNT(*) as NumRobberies, SUM(Share) as TotalEarnings
FROM info.Robber r JOIN
info.Accomplices a
on r.RobberID = a.RobberID
GROUP BY a.Nickname, a.NoYears
And the third:
WITH ActiveRobbers2 as (
SELECT a.Nickname, a.NoYears, COUNT(*) as NumRobberies, SUM(Share) as TotalEarnings
FROM info.Robber r JOIN
info.Accomplices a
on r.RobberID = a.RobberID
GROUP BY a.Nickname, a.NoYears
)
SELECT a.*
FROM ActiveRobbers2 a
WHERE NoYears = 0 AND
NumRobberies > (SELECT AVG(NumRobberies) FROM ActiveRobbers2);
Actually, I would write the third version using window functions, but I'm leaving your logic as it is in the question.

How should I create a temporary table when Union is used?

I can't use Dynamic Value bcoz of Error stating
"Lookup Error - SQL Server Database Error: Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
Here is the Scenario :
Query 1
select pr.PRDCT,sum(CASE when pr.DEFINITIONCD='NOP' and pr.PERIOD='D' then pr.PRAMOUNT else 0 END)
as 'NOP D' from PRODUCTWISE_REPORT pr group by pr.PRDCT
Query 2
select DEFINITIONTYPECD from REPORTKPIMAPTXN where DEFINITIONTYPECD='NOP' and REPORTSEQ = (select REPORTSEQ from report_m where REPORTCD='MIS_Product_Wise_Report')
Query 2 returns 'NOP'
so when I put Query 2 in Query 1 for 'NOP', it throws Error
How to resolve this when I've to User Dynamic Query 2 ?
Your second query looks it could be rewritten with a join instead of that subselect. something like this. Of course you are still going to have some issues because your first query has two columns and this has only 1 column. You will have to add another column (can be NULL) to this query before the UNION will actually work.
select r.DEFINITIONTYPECD
from REPORTKPIMAPTXN r
INNER JOIN report_m m on m.REPORTSEQ = r.REPORTSEQ
where DEFINITIONTYPECD = 'NOP'
and r.REPORTCD = 'MIS_Product_Wise_Report'

SQL query - how do I restrict to the rows I'm interested in

I have the following tables - simplified quite a bit
Table - Tests
Test
A
B
C
D
E
F
G
H
Table - TestHistory
Test Result Version
A Pass 1
A Fail 2
B Pass 2
C Fail 1
C Pass 2
D Fail 1
D Fail 2
E Fail 1
I want to get the list of tests that failed (or any status) the last time they ran. But, also the version that it was found in.
So, in the above example, I want this returned:
A Fail 2
D Fail 2
E Fail 1
I've tried a couple methods
select Test, LastResult = IsNull((Select Top 1 Result From TestHistory Where Test = Tests.Test order by Version desc), 'NOT_RUN')
from Tests
What this does, is gives me a list of all tests and then I have to go through and kick out the rows I don't want (i.e. isn't Fail). This also doesn't give me the Version it ran in.
I also tried this:
select Version, TH.Test, Result
from TestHistory as TH inner join Tests as T on TH.Test = T.Test
where Result = 'Fail'
But, then I get rows such as:
Test Result Version
C Fail 1
I don't want those because it's not the Last Result.
How can I restrict this to give me exactly what I need without a lot of data manipulation (or worse, more DB reads) after? Any help would be appreciated. Thanks!
I can't syntax check this, but it should be close:
SELECT
th.Test,
th.Result,
th.Version
FROM
TestHistory th
INNER JOIN
(
SELECT
MAX(Version) as MaxVersion,
Test
FROM
TestHistory
GROUP BY
Test
) sub ON sub.MaxVersion = th.Version AND sub.Test = th.Test
WHERE
th.result = 'Fail'
Explanation: First, in the subquery, you get the maximum version for the test. Then use a join to restrict the outer query to only return the results that match the test/version of the subquery.
Edit: forgot the WHERE clause--seems you only want rows where the most recent result is failure.
Edit based on the question in your comment:
This should give you the most recent failure, plus tests that have never run. Note that this will filter out tests that have run but have never failed (your data does not have any of these). I based this on my original query in the interest of time, but I would guess there is a more elegant way:
SELECT
t.Test,
outerSub.Result,
outerSub.Version
FROM
Test t
LEFT JOIN
(SELECT
th.Test,
th.Result,
th.Version
FROM
TestHistory th
INNER JOIN
(
SELECT
MAX(Version) as MaxVersion,
Test
FROM
TestHistory
GROUP BY
Test
) sub ON sub.MaxVersion = th.Version AND sub.Test = th.Test
) outerSub on outerSub.Test = t.Test
WHERE
outerSub.result = 'Fail' OR outerSub.Test IS NULL
Small correction can be added to the above solution.
In case when You need to receive the results in the Test order, the query can be transformed as below:
SELECT src.Test, src.Result, src.Version
FROM
(
SELECT th.Version, th.Test, th.Result,
ROW_NUMBER() over(partition by th.Test order by th.Version desc) as RowNum
FROM dbo.TestHistory as th
) src
WHERE src.RowNum = 1 and src.Result = 'Fail'
order by src.Test;
Among this, the query will return the set in the needed columns' order.
How about:
select th.* from testHistory th
where th.result = 'fail' -- this part, according to you, being optional
and th.version =
(select max(t.version) from testhistory t
where t.test = th.test);

SQL Reporting count of parameter in a column

I am working in SSRS 3.0 with a SQL table including the following fields:
ApptID BookedBy ConfirmedBy CancelledBy
I also have a parameter setup to select which users to filter by (matches data in the BookedBy, ConfirmedBy and CancelledBy columns) called #Scheduler (which is a multi vale parameter/array).
I need to get a count for booked, confirmed and scheduled for how many times any value in the Scheduler parameter shows up in that column.
Basically:
COUNT(BookedBy IN (#Scheduler)) AS BookedCount
Can anyone help me out with the syntax for doing this?
Try this
SELECT Count(BookedBy = #Scheduler) as [BookedCount],
Count(ConfirmedBy = #Scheduler) as [ConfirmedCount],
Count(CancelledBy = #Scheduler) as [CancelledCount]
FROM tablename
WHERE BookedBy = #Scheduler OR
ConfirmedBy = #Scheduler OR
CancelledBy = #Scheduler
NB - Not tested might contain typos
If your input is a list separated by commas you can convert that to a table. See a reference like this:
http://www.projectdmx.com/tsql/sqlarrays.aspx
For this use case I'd recommend one of the solutions that saves the result in a CTE (since you only need to convert your input once and this will be fastest)
Then you could use that table (called sTable with column name) like this:
SELECT Count(Bo.Name) as [BookedCount],
Count(Co.Name) as [ConfirmedCount],
Count(Ca.Name) as [CancelledCount]
FROM tablename
LEFT JOIN sTable Bo ON BookedBy = Bo.name
LEFT JOIN sTable Co ON ConfirmedBy = Co.name
LEFT JOIN sTable Ca ON CancelledBy = Ca.name
I guess this will work but it does not seem as nice as the others:
SELECT (SELECT COUNT(*) FROM table WHERE BookedBy in (#Scheduler)) AS [BookedCount],
(SELECT COUNT(*) FROM table WHERE ConfirmedBy in (#Scheduler)) as [ConfirmedCount],
(SELECT COUNT(*) FROM table WHERE CancelledBy in (#Scheduler)) as [CancelledCount]

MAX Subquery in SQL Anywhere Returning Error

In sqlanywhere 12 I wrote the following query which returns two rows of data:
SELECT "eDatabase"."Vendor"."VEN_CompanyName", "eDatabase"."OrderingInfo"."ORD_Timestamp"
FROM "eDatabase"."OrderingInfo"
JOIN "eDatabase"."Vendor"
ON "eDatabase"."OrderingInfo"."ORD_VEN_FK" = "eDatabase"."Vendor"."VEN_PK"
WHERE ORD_INV_FK='7853' AND ORD_DefaultSupplier = 1
Which returns:
'**United Natural Foods IN','2018-02-07 15:05:15.513'
'Flora ','2018-02-07 14:40:07.491'
I would like to only return the row with the maximum timestamp in the column "ORD_Timestamp". After simply trying to select by MAX("eDatabase"."OrderingInfo"."ORD_Timestamp") I found a number of posts describing how that method doesn't work and to use a subquery to obtain the results.
I'm having difficulty creating the subquery in a way that works and with the following query I'm getting a syntax error on my last "ON":
SELECT "eDatabase"."Vendor"."VEN_CompanyName", "eDatabase"."OrderingInfo"."ORD_Timestamp"
FROM ( "eDatabase"."OrderingInfo"
JOIN
"eDatabase"."OrderingInfo"
ON "eDatabase"."Vendor"."VEN_PK" = "eDatabase"."OrderingInfo"."ORD_VEN_FK" )
INNER JOIN
(SELECT "eDatabase"."Vendor"."VEN_CompanyName", MAX("eDatabase"."OrderingInfo"."ORD_Timestamp")
FROM "eDatabase"."OrderingInfo")
ON "eDatabase"."Vendor"."VEN_PK" = "eDatabase"."OrderingInfo"."ORD_VEN_FK"
WHERE ORD_INV_FK='7853' AND ORD_DefaultSupplier = 1
Does anyone know how I can adjust this to make the query correctly select only the max ORD_Timestamp row?
try this:
SELECT TOP 1 "eDatabase"."Vendor"."VEN_CompanyName", "eDatabase"."OrderingInfo"."ORD_Timestamp"
FROM "eDatabase"."OrderingInfo"
JOIN "eDatabase"."Vendor"
ON "eDatabase"."OrderingInfo"."ORD_VEN_FK" = "eDatabase"."Vendor"."VEN_PK"
WHERE ORD_INV_FK='7853' AND ORD_DefaultSupplier = 1
order by "ORD_Timestamp" desc
this orders them biggest on to and say only hsow the top row