postgresql order by - postgresql

My table has a 'number' column which ranges from 0 to around 1000. I want to select everything from the table while ordering it by number. However, I want the rows with number=0 to appear last. I could do it in two queries:
select * from channel_table where number > 0 order by number;
select * from channel_table where number = 0;
But I'm guessing there is a one-line solution?

As you only have one exceptional case, you can use nullif with the nulls clause. Otherwise you would need to order by various case statements to get more complicated ordering logic.
select * from channel_table
order by nullif(number, 0) nulls last

Is this to simple?
select * from channel_table order by number desc;

Related

Looking to reduce postgres queries to one query

I want to reduce the number of queries I'm making to my postgres database.
Currently I have this
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < yesterday ORDER BY tick DESC LIMIT 1;
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < last_week ORDER BY tick DESC LIMIT 1;
Is there some way I can make this into one query. Or is there even any need to reduce it?
Thanks
You can simply use the query with the greater date of tick. The first query already includes the results coming from the second one.
Just use the first query and you are good to go.
select * from token_balances where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a' AND tick < '2022-05-26T14:13:42.914Z' ORDER BY tick DESC LIMIT 1;
Rather than use yesterday (which is a valid reference) and last_week (which is not valid) just convert to date arithmetic. You can use simple date subtraction:
select *
from token_balances
where asset_id = '36f813e4-403a-4246-a405-8efc0cbde76a'
and tick < current_date - :num_days
order by tick desc
limit 1;

How to get a sum of all rows that meets condition in postgres

I am trying to return sums with their specific conditions.
SELECT
COUNT(*),
SUM("transactionTotal" WHERE "entryType"=sold) as soldtotal,
SUM(case when "entryType"=new then "transactionTotal" else 0 end) as newtotal
FROM "MoneyTransactions"
WHERE cast("createdAt" as date) BETWEEN '2020-10-08' AND '2020-10-09'
I am trying to sum up the rows with "entryType"=sold and "entryType"=new and return those values separately.
obviously both my logic are wrong.
can someone lend a hand.
You were on the right track to use conditional aggregation, but your syntax is slightly off. Try this version:
SELECT
COUNT(*) AS total_cnt,
SUM(transactionTotal) FILTER (WHERE entryType = 'sold') AS soldtotal,
SUM(transactionTotal) FILTER (WHERE entryType = 'new') AS newtotal
FROM MoneyTransactions
WHERE
createdAt::date BETWEEN '2020-10-08' AND '2020-10-09';
Note: Assuming your createdAt column already be date, then there is no point in casting it. If it is text, then yes you would need to convert it, but you might have to use TO_DATE depending on its format.

it is possible to concatenate one result set onto another in a single query?

I have a table of Verticals which have names, except one of them is called 'Other'. My task is to return a list of all Verticals, sorted in alpha order, except with 'Other' at the end. I have done it with two queries, like this:
String sqlMost = "SELECT * from core.verticals WHERE name != 'Other' order by name";
String sqlOther = "SELECT * from core.verticals WHERE name = 'Other'";
and then appended the second result in my code. Is there a way to do this in a single query, without modifying the table? I tried using UNION
(select * from core.verticals where name != 'Other' order by name)
UNION (select * from core.verticals where name = 'Other');
but the result was not ordered at all. I don't think the second query is going to hurt my execution time all that much, but I'm kind of curious if nothing else.
UNION ALL is the usual way to request a simple concatenation; without ALL an implicit DISTINCT is applied to the combined results, which often causes a sort. However, UNION ALL isn't required to preserve the order of the individual sub-results as a simple concatenation would; you'd need to ORDER the overall UNION ALL expression to lock down the order.
Another option would be to compute an integer order-override column like CASE WHEN name = 'Other' THEN 2 ELSE 1 END, and ORDER BY that column followed by name, avoiding the UNION entirely.

Conditional OR in the SQL Server Join – Multi-Value Parameters

I have an SSRS report with 4 parameters, two of which are multi-value parameters (#material and #color using VARCHAR(MAX) datatype in SQL Server 2008 R2). I am using a split function to return the value as a comma separated:
SELECT *
FROM MyView
WHERE height > 200
AND width > 100
AND (
material IN (SELECT Item FROM [dbo].[MySplitFunction] (#material, ',')) OR
color IN (SELECT Item FROM [dbo].[MySplitFunction] (#color, ','))
)
(The code above would return 50 records)
The problem with this approach is that these two multi-value parameters have around of 1,500 different colors and materials and degrade the performance. Sometimes, it takes more than 40 minutes to return the results (row count in the view around 600,000).
I tried a different approach where I used a temp table and used it in the JOIN instead of the WHERE clause:
SELECT Item
INTO #TempTable
FROM [dbo].[MySplitFunction] (#material, ',')
SELECT *
FROM MyView
INNER JOIN ON MyView.Item = #TempTable.Item
WHERE height > 200
AND width > 100
AND material IN (SELECT Item FROM [dbo].[MySplitFunction] (#material, ','))
(The code above would return 7 records only, but the performance is much better)
My question is how can I return the same number of records (50 rows) using the second approach by adding the other #color parameter and allowing the OR condition? So in the SSRS report, the user can multi select these two parameters and the query will return #material = values OR #color = Values.
I am open to any other approach as long as it speeds up the query and allows the OR condition for the two multi-value parameters (#material, #color).
Thanks!
Something like the following might do the trick. I'm not sure I have the syntax precisely right, and it wants further testing and analysis that I can't do without the proper structures and data...
SELECT
from MyVeiew
where height > 200
and width > 100
and (exists (select Item
from dbo.MySplitFunction(#material, ',')
where Item = material)
or exists (select Item
from dbo.MySplitFunction(#color, ',')
where Item = color)
)
This performs two correlated subqueries on nested function calls. Exists checks are generally faster than in lookups in these situations. The syntax bit that worries me is the "and (exists" bit -- that's the parenthesis for the OR clause, and combined with exists it looks a bit wonky.
I think it should do what you want, but testing is definitely called for.
I mistrust that or clause. To get rid of it, try this and see what happens:
SELECT * -- Better with specific columns
from MyView
where height > 200
and width > 100
and exists (select Item
from dbo.MySplitFunction(#material, ',')
where Item = material)
UNION select *
from MyView
where height > 200
and width > 100
and exists (select Item
from dbo.MySplitFunction(#color, ',')
where Item = color)
This runs and combines two queries, removing all duplicates -- pretty much the same as the OR clause would.
Next thing to check would be reviewing table sizes and checking indexes. You're filtering results on (only!) columns height, width, material, and color; if the table is huge, appropriate index would help here.

Postgresql upper limit of a calculated field

is there a way to set an upper limit to a calculation (calculated field) which is already in a CASE clause? I'm calculating percentages and, obviously, don't want the highest value exceed '100'.
If it wasn't in a CASE clause already, I'd create something like 'case when calculation > 100.0 then 100 else calculation end as needed_percent' but I can't do it now..
Thanks for any suggestions.
I think using least function will be the best option.
select least((case when ...), 100) from ...
There is a way to set an upper limit on a calculated field by creating an outer query. Check out my example below. The inner query will be the query that you have currently. Then just create an outer query on it and use a WHERE clause to limit it to <= 1.
SELECT
z.id,
z.name,
z.percent
FROM(
SELECT
id,
name,
CASE WHEN id = 2 THEN sales/SUM(sales) ELSE NULL END AS percent
FROM
users_table
) AS z
WHERE z.percent <= 1