How to build this query in postgresql - postgresql

Supposing I have this data. How to query this in such a way that will result to this in an efficient way. It will sum the qty of all OUT less the IN per item.

Try this query:
select
"Desc",
sum(case when Type = 'out' then Qty else 0 end) -
sum(case when Type = 'in' then Qty else 0 end)
from yourTable
group by "Desc"
Desc
Note that DESC is a reserved keyword and you should not be naming your databases, tables, or columns using it. I think you would have to escape it in double quotes to get the query to run.

select desc, sum(case Type when 'out' then Qty else -Qty end) from test group by desc;
this will be faster.

Or
SELECT Description, SUM(Case WHEN rowtype = 'in' then 1 else -1 end * Qty) as rowqty FROM yourtable GROUP BY description
Note that it is not a good idea to use reserved words as column names. DESC and TYPE are asking for trouble!

Related

Postgresql, set order by desc or asc depending on variable parse into function

I have a function that takes product pricing data from today and yesterday and works out the difference, orders it by price_delta_percentage and then limits to 5. Now currently I order by price_delta_percentage DESC which returns the top 5 products that have increased in price since yesterday.
I would like to parse in a variable - sort - to change the function to either sort by DESC, or ASC. I have tried to use IF statements and get syntax errors and CASE statements which states that price_delta_percentage doesn't exist.
Script:
RETURNS TABLE(
product_id varchar,
name varchar,
price_today numeric,
price_yesterday numeric,
price_delta numeric,
price_delta_percentage numeric
)
LANGUAGE 'sql'
COST 100
STABLE STRICT PARALLEL SAFE
AS $BODY$
WITH cte AS (
SELECT
product_id,
name,
SUM(CASE WHEN rank = 1 THEN trend_price ELSE NULL END) price_today,
SUM(CASE WHEN rank = 2 THEN trend_price ELSE NULL END) price_yesterday,
SUM(CASE WHEN rank = 1 THEN trend_price ELSE 0 END) - SUM(CASE WHEN rank = 2 THEN trend_price ELSE 0 END) as price_delta,
ROUND(((SUM(CASE WHEN rank = 1 THEN trend_price ELSE NULL END) / SUM(CASE WHEN rank = 2 THEN trend_price ELSE NULL END) - 1) * 100), 2) as price_delta_percentage
FROM (
SELECT
magic_sets_cards.name,
pricing.product_id,
pricing.trend_price,
pricing.date,
RANK() OVER (PARTITION BY product_id ORDER BY date DESC) AS rank
FROM pricing
JOIN magic_sets_cards_identifiers ON magic_sets_cards_identifiers.mcm_id = pricing.product_id
JOIN magic_sets_cards ON magic_sets_cards.id = magic_sets_cards_identifiers.card_id
JOIN magic_sets ON magic_sets.id = magic_sets_cards.set_id
WHERE date BETWEEN CURRENT_DATE - days AND CURRENT_DATE
AND magic_sets.code = set_code
AND pricing.trend_price > 0.25) p
WHERE rank IN (1,2)
GROUP BY product_id, name
ORDER BY price_delta_percentage DESC)
SELECT * FROM cte WHERE (CASE WHEN price_today IS NULL OR price_yesterday IS NULL THEN 'NULL' ELSE 'VALID' END) !='NULL'
LIMIT 5;
$BODY$;sql
CASE Statement:
ORDER BY CASE WHEN sort = 'DESC' THEN price_delta_percentage END DESC, CASE WHEN sort = 'ASC' THEN price_delta_percentage END ASC)
Error:
ERROR: column "price_delta_percentage" does not exist
LINE 42: ORDER BY CASE WHEN sort = 'DESC' THEN price_delta_percenta...
You can't use CASE to decide between ASC and DESC like that. Those labels are not data, they are part of the SQL grammar. You would need to do it by combining the text into a string and then executing the string as a dynamic query, which means you would need to use pl/pgsql, not SQL
But since your column is numeric, you could just order by the product of the column and an indicator variable which is either 1 or -1.

How to get unique date from multiple dates based on condition in PostgreSQL?

How do I get unique dates from the table "timelog" for the dates where isParent is not 1.
In the table, 2022-01-10 should not come as the result as this date has isParent as 1.
So far, I have written query like this -
SELECT DISTINCT session::date
FROM timelog
WHERE id IN (SELECT id FROM timelog WHERE isParent = 0)
Obviously, this is not working as intended. What changes do I need to make in this query to make it work?
You can try to use condition aggregate function in HAVING, let your condition which didn't any isParent = 1 date on CASE WHEN
SELECT session::date
FROM timelog
GROUP BY session::date
HAVING COUNT(CASE WHEN isParent = 1 THEN 1 END) = 0
sqlfiddle
SELECT session::date
FROM timelog
GROUP BY session::date
HAVING COUNT(*) filter(where isParent = 1) = 0
SELECT session::date
FROM timelog
GROUP BY session::date
HAVING NOT ARRAY_AGG(isParent) && ARRAY[1]; -- NOT in array containing 1
Something along the lines of
SELECT DISTINCT session::date FROM timelog WHERE isParent = 0
should work. All you are looking for is unique dates where the parent is 0 Correct? No aggregation needs to be applied ?

Mssql err: Column 'dbo.History.Email' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

when executing this MSSQL statement it forces me to add the CASE WHEN field History.Email in the group by field, however I need to group only on a date field.
Any idea how this can be solved? (sql 2008)
SELECT TOP (100)
convert(varchar, VisitDate, 111) as Date,
SUM(AmountLoaded) AS Loaded,
CASE when History.Email is null then
SUM(AmountCash)
else SUM(AmountToPay)
end AS Total,
CASE when History.Email is null then
0
else SUM(AmountToPay)
end AS App,
SUM(AmountToPay) AS Consumed
FROM dbo.History
GROUP BY convert(varchar, VisitDate, 111)
order by 1 desc
When performing conditional aggregation you need to put the entire CASE expression in an aggregate, not just the expression(s) you want to aggregate:
SELECT TOP (100)
CONVERT(varchar(10), H.VisitDate, 111) AS [Date], --Always specify a length/precision/scale for your data types
SUM(H.AmountLoaded) AS Loaded,
SUM(CASE WHEN H.Email IS NULL THEN H.AmountCash ELSE H.AmountToPay END) AS Total,
SUM(CASE WHEN H..Email IS NULL THEN 0 ELSE H.AmountToPay END) AS App,
SUM(H.AmountToPay) AS Consumed
FROM dbo.History H
GROUP BY CONVERT(varchar(10), H.VisitDate, 111) --Always specify a length/precision/scale for your data types
ORDER BY [Date] DESC; --Be specific, don't use ordinal positions
Note, as well, I suggest not converting your column VisitDate to a varchar in your query. If you want to display the data in a specific format then that's something for your presentation layer to do, not the RDBMS. Fortunately, as style 111 is yyyy/MM/dd then the order of the data isn't affected. If, however, you want to simply truncate the time portion of your value, convert it to a date so that the value continues to remain strongly typed.
By definition any nonaggregate fields should be in the group by clause. You can write that as:
SELECT TOP (100)
convert(varchar, VisitDate, 111) as Date,
SUM(AmountLoaded) AS Loaded,
SUM(CASE when History.Email is null
then AmountCash
else AmountToPay end) AS Total,
SUM(CASE when History.Email is null
then 0
else AmountToPay
end) AS App,
SUM(AmountToPay) AS Consumed
FROM dbo.History
GROUP BY convert(varchar, VisitDate, 111)
order by 1 desc

How to aggregate count of unique values in a column of pgsql table & display them in columns?

In Postgresql, one of my columns contain only contain one of 5 values. One of those 5 values is "Matured". I am trying to write a query that counts how many times "Matured" appears in that column for a specific cut of the whole table.
The following query produces the error: syntax error at or near "Matured".
The query:
select count(case when stagename is Matured end) from db_table
ALTERNATIVELY, I have the following result:
"Matured";694
"Credit Approved";3
"Delinquent";572
"Current";1356
"Canceled";16
using the query:
select distinct stagename,
sum(case when stagename is not null then 1 else 0 end)
from db_table
group by stagename
But I need that result in columns not rows. Like so:
|Matured |Credit Approved |Delinquent |Current |Canceled
stage_count |694 |3 |572 |1356 |16
Any ideas?
You have missed single quotes for Matured in your query as well as case when statement is not correct that is why it is returning syntax error. You can use below query to get required result.
You can also read about crosstab function of postgres.
select count(case when stagename = 'Matured' then 1 end),
count(case when stagename = 'Credit Approved' then 1 end),
count(case when stagename = 'Delinquent' then 1 end),
count(case when stagename = 'Current' then 1 end),
count(case when stagename = 'Canceled' then 1 end)
from db_table
Note: If your column contains leading and trailing space then use trim function with your column name and then compare.

How to affect a single status in a where clause with multiple statuses

Here's my query that returns a count of policies from year to date. I want to know how I can go about doing a case when statement that affects only the status that is equal to 'active'.
select distinct
count(pol3.CT_ID + pol3.CT_NSID) as 'YTD',
pol3.ct_status
from contract pol3
where (pol3.CT_Status = 'Quote' or pol3.ct_status='Active' or pol3.ct_status='Declined')
and Year(pol3.CT_Subscription_Date)>= datediff(year,(DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), getdate())
group by pol3.ct_status
Here's the current output:
I tried doing
select distinct
count(pol3.CT_ID + pol3.CT_NSID) as 'YTD',
pol3.ct_status
from contract pol3
where (pol3.CT_Status = 'Quote' or pol3.ct_status='Active' or pol3.ct_status='Declined')
and CT_ORIGINAL_QUOTE_ID is not null
and CT_ORIGINAL_QUOTE_NSID is not null
and Year(pol3.CT_Subscription_Date)>= datediff(year,(DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), getdate())
group by pol3.ct_status
This is obviously wrong because its targeting all three statuses. How do I go about making ct_original_* target only the active status? Any clues or pointers would be highly appreciated.
Use a CASE statement in the SELECT to return a 1 or 0 and then SUM the values returned.
SELECT pol3.ct_status, SUM(CASE
WHEN ct_status IN ('Declined','Quote') THEN 1 // all of these should count as one policy
WHEN ct_status = 'Active' and CT_ORIGINAL_QUOTE_ID is not null and CT_ORIGINAL_QUOTE_NSID is not null THEN 1 // if data in ID and NSID and active count it
ELSE 0 // all others count as nothing
END)
from contract pol3
where (pol3.CT_Status = 'Quote' or pol3.ct_status='Active' or pol3.ct_status='Declined')
and Year(pol3.CT_Subscription_Date)>= datediff(year,(DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)), getdate())
group by pol3.ct_status