How to write this SQL Order By Clause - sql

I have a SQL Query which I am trying to write and am now a bit stuck on how to write the order by clause.
Basically the table I am selecting from has items with a severity value. I want to select these items and order them so that the Severity column is ordered Severity 1-4 then 0 and the log date is descending for each.
Severity 1 is highest 4 is lowest and 0 respresents an unassigned severity, I need to display these items Highest severity, oldest item first, lowest severity, newest item last.
My query so far:
SELECT
[TicketID],
[type],
[Product],
[Description],
[LoggedBy],
[LogDate],
[Department],
[AssignedTo],
[Severity],
[Status],
[LastUpdatedBy],
[LastUpdatedDate]
FROM
SupportTicketsTbl
WHERE
TicketID NOT IN
(
SELECT
tck.ticketID
FROM
SupportTicketsTbl tck
JOIN
tblTicketsInEvents tie
ON
tck.TicketID = tie.ticketID
JOIN
tblSupportEvent ev
ON
tie.eventID = ev.id
where
ev.type = 1
)
AND
Status <> 'Complete'
I guess the easiest way is to create a table variable and select all the Items that are not 0 into it in the order I want, then select all the 0 items into my table variable, and finally just select everything back out of the table variable, but this seems a bit messy so im wondering if there is a more elegant solution?
Thanks

Since you didn't like the UNION answer, and I'm not sure if UNION is guaranteed to preserve order...
ORDER BY CASE WHEN severity = 0 THEN 999 ELSE Severity END, date

You can order by a case statement like this:
ORDER BY CASE Severity WHEN 0 THEN 1 ELSE 2 END, Severity

First, Select all of the ones with severity levels 1-4 using a standard orderby clause, then union the results with a second query that selects only the ones with severity level 0.

Unless I'm very much mistaken, it's something like this:
ORDER BY severity DESC, date DESC
Insert that line into your SQL.
This will sort the data by Severity first, and if they have the same severity, then sort it according to date.

Related

SQL - Count new entries based on last date

I have a table with the follow structure
ID ReportDate Object_id
What I need to know, is the count of new and count of old (Object id's)
For example: If I have the data below:
I want the following output grouped by ReportDate:
I thought a way doing it using a Where clause based on date, however i need the data for all the dates I have in the table. To see the count of what already existed in the previous report and what is new at that report. Any Ideas?
Edit: New/Old definition- New would be the records that never appeared before that report run date and appeared on this one, whereas old is the number of records that had at least one match in previous dates. I'll edit the post to include this info.
managed to do it using a left join. Below is my solution in case it helps anyone in the future :)
SELECT table.ReportRunDate,
-1*sum(table.ReportRunDate = new_table.init_date) as count_new,
-1*sum(table.ReportRunDate <> new_table.init_date) as count_old,
count(*) as count_total
FROM table LEFT JOIN
((SELECT Object_ID, min(ReportRunDate) as init_date
FROM table
GROUP By OBJECT_ID) as new_table)
ON table.Object_ID = new_table.Object_ID
GROUP BY ReportRunDate
This would work in Oracle, not sure about ms-access:
SELECT ReportDate
,COUNT(CASE WHEN rnk = 1 THEN 1 ELSE NULL END) count_of_new
,COUNT(CASE WHEN rnk <> 1 THEN 1 ELSE NULL END)count_of_old
FROM (SELECT ID
,ReportDate
,Object_id
,RANK() OVER (PARTITION BY Object_id ORDER BY ReportDate) rnk
FROM table_name)
GROUP BY ReportDate
Inner query should rank each occurence of object_id based on the ReportDate so the 1st occurrence of certain object_id will have rank = 1, the next one rank = 2 etc.
Then the outer query counts how many records with rank equal/not equal 1 are the within each group.
I assumed that 1 object_id can appear only once within each reportDate.

Retrieve the total number of orders made and the number of orders for which payment has been done

Retrieve the total number of orders made and the number of orders for which payment has been done(delivered).
TABLE ORDER
------------------------------------------------------
ORDERID QUOTATIONID STATUS
----------------------------------------------------
Q1001 Q1002 Delivered
O1002 Q1006 Ordered
O1003 Q1003 Delivered
O1004 Q1006 Delivered
O1005 Q1002 Delivered
O1006 Q1008 Delivered
O1007 Q1009 Ordered
O1008 Q1013 Ordered
Unable to get the total number of orderid i.e 8
select count(orderid) as "TOTALORDERSCOUNT",count(Status) as "PAIDORDERSCOUNT"
from orders
where status ='Delivered'
The expected output is
TOTALORDERDSCOUNT PAIDORDERSCOUNT
8 5
I think you want conditional aggregation:
select count(*) as TOTALORDERSCOUNT,
sum(case when status = 'Delivered' then 1 else 0 end) as PAIDORDERSCOUNT
from orders;
Try this-
SELECT COUNT(ORDERID) TOTALORDERDSCOUNT,
SUM(CASE WHEN STATUS = 'Delivered' THEN 1 ELSE 0 END ) PAIDORDERSCOUNT
FROM ORDER
You can also use COUNT in place of SUM as below-
SELECT COUNT(ORDERID) TOTALORDERDSCOUNT,
COUNT(CASE WHEN STATUS = 'Delivered' THEN 1 ELSE NULL END ) PAIDORDERSCOUNT
FROM ORDER
you could use cross join between the two count
select count(orderid) as TOTALORDERSCOUNT, t.PAIDORDERSCOUNT
from orders
cross join (
select count(Status) PAIDORDERSCOUNT
from orders where Status ='Delivered'
) t
What I've used in the past for summarizing totals is
SELECT
count(*) 'Total Orders',
sum( iif( orders.STATUS = 'Delivered', 1, 0 ) ) 'Total Paid Orders'
FROM orders
I personally don't like using CASE WHEN if I don't have to. This logic may look like its a little too much for a simple summation of totals, but it allows for more conditions to be added quite easily and also just involves less typing, at least for what I use this regularly for.
Using the iif( statement to set up the conditional where you're looking for all rows in the STATUS column with the value 'Delivered', with this set up, if the status is 'Delivered', then it marks it stores a value of 1 for that order, and if the status is either 'Ordered' or any other value, including null values or if you ever need a criteria such as 'Pending', it would still give an accurate count.
Then, nesting this within the 'sum' function totals all of the 1's denoted from your matched values. I use this method regularly for report querying when there's a need for many conditions to be narrowed down to a summed value. This also opens up a lot of options in the case you need to join tables in your FROM statement.
Also just out of personal preference and depending on which SQL environment you're using this in, I tend to only use AS statements for renaming when absolutely necessary and instead just denote the column name with a single quoted string. Does the same thing, but that's just personal preference.
As stated before, this may seem like it's doing too much, but for me, good SQL allows for easy change to conditions without having to rewrite an entire query.
EDIT** I forgot to mention using count(*) only works if the orderid's are all unique values. Generally speaking for an orders table, orderid is an expected unique value, but just wanted to add that in as a side note.
SELECT DISTINCT COUNT(ORDERID) AS [TOTALORDERSCOUNT],
COUNT(CASE WHEN STATUS = 'ORDERED' THEN ORDERID ELSE NULL END) AS [PAIDORDERCOUNT]
FROM ORDERS
TotalOrdersCount will count all distinct values in orderID while the case statement on PaidOrderCount will filter out any that do not have the desired Status.

SQLite3 database conditional summing

I am looking to order a list of keys based on the number of orders placed from a database containing order requests. Basically, on table, call it orders(o_partkey, o_returnflag) I am trying to get the total number of returns for each order. I have tried many variations of the following snippet with the goal schema returnlist(partkey, numreturns):
select O.o_partkey as partkey,
count(case when O.o_returnflag = 'R' then 1 else 0 end) as numreturns
from orders O
orderby quantity_returned desc;
I am very new to SQLite and am just jumping into the basics. This is an adjustment of a homework question (the actual question is more complex) but I have simplified down the issue I am having.
Consider using a derived table subquery with SUM() as the aggregate function:
SELECT dT.partkey, dT.numreturns
FROM
(SELECT O.o_partkey as partkey,
SUM(CASE WHEN O.o_returnflag = 'R' THEN 1 ELSE 0 END) as numreturns
FROM [ORDER] O
GROUP BY O.o_partkey) AS dT
ORDER BY dT.numreturns DESC;
Be sure to bracket name of table as [ORDER] is an SQLite key word.
Your problem is that COUNT counts rows, so it counts both 0 and 1 values.
You are not interested in any other rows, so you can just filter out the returns with WHERE:
SELECT o_partkey AS partkey,
COUNT(*) AS numreturns
FROM orders
WHERE o_returnflag = 'R'
ORDER BY 2 DESC;

Get a unique row based on a column item in SQL query

I have a sql query to return IT tickets and their satisfaction scores however because of the way our ticketing system works this sometimes returns 2 rows for a ticket reference - 1 with a "Not Rated" rating and one with the real rating.
My question is, is there a way to get the query to only return a single row for each Ticket Reference and only return the "Not Rated" rating if another rating does not exist. i.e. when there are 2 rows with the same ticket reference and one has a rating of "Excellent" and one has a rating of "Not Rated" that it only returns the row with the "Excellent" rating. Any with only 1 row that is "Not Rated" should be returned.
The query looks like this so far -
SELECT DISTINCT
t.Rating_Date,
t.id AS 'Ticket Reference',
[Rating]
= CASE
When tt.[rating_id] = 20 then 'Poor'
When tt.[rating_id] = 15 then 'Average'
When tt.[rating_id] = 17 then 'Good'
When tt.[rating_id] = 6 then 'Excellent'
else 'Not Rated'
END,
[subject],
[priority],
[status],
uu.name,
[assignee_id],
[Location],
[technology]
FROM
[DB1].[dbo].[table1] t
INNER JOIN [DB1].[dbo].[table2] tt
ON t.id=tt.ticket_id
LEFT JOIN [DB1].[dbo].[table3] uu
ON t.assignee_id=uu.id
WHERE
t.rating_date > '2013-07-01'
AND status = 'closed'
AND location = 'UK'
AND technology <> 'Not Known'
AND group_id = '5678912'
ORDER BY
t-rating_date
I think you can wrap the first query, order it by rating_id (if Not rated value is 0 you should order DESC, if Not rated is the highest you should order ASC) and then select the first record using the TOP function of sql-server.
I suppose that the following logic can be implemented here. Please, be aware of that I am using sample data and in order to implement the solution you should understand the idea.
Let's say we have the following data:
Note: I am using id "0" for "Not Rated" status.
If I have understand your need correctly, the output data should be something like this:
As you can see from the screenshot above, for Tickets with IDs 1 and 4 we have "Not Rated" records, but we are not showing them. The "Not Rated" state is displayed only for Ticket with ID 3.
The solution:
;WITH DataSource AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [TicketID] ORDER BY [TicketRateID] DESC) AS [RowNumber]
,[TicketID]
,[TicketRateID]
FROM #DataSource
)
SELECT [TicketID]
,[RowNumber]
,[TicketRateID]
FROM DataSource
The SQL statement above is using ROW_NUMBER function in order to create a unique ID for each set of records for given tickets. We are sorting the records with DESC directive in order to be sure the "0"/"Not rated" records will have bigger ID.
The output of previous statement is:
As you can see from the screenshot above, we need to display only this records with RowNumber equal to 1. This is done simple with WHERE clause.
Follows, full working example:
SET NOCOUNT ON
GO
DECLARE #DataSource TABLE
(
[TicketID] TINYINT
,[TicketRateID] TINYINT
)
INSERT INTO #DataSource ([TicketID],[TicketRateID])
VALUES (1,6)
,(1,0)
,(2,20)
,(3,0)
,(4,0)
,(4,15)
;WITH DataSource AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY [TicketID] ORDER BY [TicketRateID] DESC) AS [RowNumber]
,[TicketID]
,[TicketRateID]
FROM #DataSource
)
SELECT [TicketID]
,[RowNumber]
,[TicketRateID]
FROM DataSource
WHERE [RowNumber] = 1
SET NOCOUNT OFF
GO

filtering rows by checking a condition for group in one statement only

I have the following statement:
SELECT
(CONVERT(VARCHAR(10), f1, 120)) AS ff1,
CONVERT(VARCHAR(10), f2, 103) AS ff2,
...,
Bonus,
Malus,
ClientID,
FROM
my_table
WHERE
<my_conditions>
ORDER BY
f1 ASC
This select returns several rows for each ClientID. I have to filter out all the rows with the Clients that don't have any row with non-empty Bonus or Malus.
How can I do it by changing this select by one statement only and without duplicating all this select?
I could store the result in a #temp_table, then group the data and use the result of the grouping to filter the temp table. - BUT I should do it by one statement only.
I could perform this select twice - one time grouping it and then I can filter the rows based on grouping result. BUT I don't want to select it twice.
May be CTE (Common Table Expressions) could be useful here to perform the select one time only and to be able to use the result for grouping and then for selecting the desired result based on the grouping result.
Any more elegant solution for this problem?
Thank you in advance!
Just to clarify what the SQL should do I add an example:
ClientID Bonus Malus
1 1
1
1 1
2
2
3 4
3 5
3 1
So in this case I don't want the ClientID=2 rows to appear (they are not interesting). The result should be:
ClientID Bonus Malus
1 1
1
1 1
3 4
3 5
3 1
SELECT Bonus,
Malus,
ClientID
FROM my_table
WHERE ClientID not in
(
select ClientID
from my_table
group by ClientID
having count(Bonus) = 0 and count(Malus) = 0
)
A CTE will work fine, but in effect its contents will be executed twice because they are being cloned into all the places where the CTE is being used. This can be a net performance win or loss compared to using a temp table. If the query is very expensive it might come out as a loss. If it is cheap or if many rows are being returned the temp table will lose the comparison.
Which solution is better? Look at the execution plans and measure the performance.
The CTE is the easier, more maintainable are less redundant alternative.
You haven't specified what are data types of Bonus and Malus columns. So if they're integer (or can be converted to integer), then the query below should be helpful. It calculates sum of both columns for each ClientID. These sums are the same for each detail line of the same client so we can use them in WHERE condition. Statement SUM() OVER() is called "windowed function" and can't be used in WHERE clause so I had to wrap your select-list with a parent one just because of syntax.
SELECT *
FROM (
SELECT
CONVERT(VARCHAR(10), f1, 120) AS ff1,
CONVERT(VARCHAR(10), f2, 103) AS ff2,
...,
Bonus,
Malus,
ClientID,
SUM(Bonus) OVER (PARTITION BY ClientID) AS ClientBonusTotal,
SUM(Malus) OVER (PARTITION BY ClientID) AS ClientMalusTotal
FROM
my_table
WHERE
<my_conditions>
) a
WHERE ISNULL(a.ClientBonusTotal, 0) <> 0 OR ISNULL(a.ClientMalusTotal, 0) <> 0
ORDER BY f1 ASC