replacement for cast and collect in oracle - sql

I want to convert the below statement into normal query.
SELECT CAST(COLLECT(warehouse_name ORDER BY warehouse_name)
AS warehouse_name_t) "Warehouses"
FROM warehouses;
How to do this?
I tried some of the things but could not succeed. Please help.

If you want ANSI SQL and do not want a collection but want the values as rows:
SELECT warehouse_name
FROM Warehouses
ORDER BY warehouse_name
If you want to aggregate the rows into a single row and want a delimited single string then use LISTAGG:
SELECT LISTAGG(warehouse_name, ',') WITHIN GROUP (ORDER BY warehouse_name)
AS warehouses
FROM Warehouses
If you want a collection data-type then CAST and COLLECT are standard built-in functions and are exactly what you should be using:
SELECT CAST(
COLLECT(warehouse_name ORDER BY warehouse_name)
AS warehouse_name_t
) AS Warehouses
FROM warehouses;
db<>fiddle here

Related

Postgres Aggregate over unnest

I have a query like the following:
select count(unnest(regexp_matches(column_name, regex)))
from table_name group by unnest(regexp_matches(column_name, regex));
The above query gives the following error:
ERROR: aggregate function calls cannot contain set-returning function calls
Hint: You might be able to move the set-returning function into a LATERAL FROM item.
I know I can first calculate unnested values by nesting a select query in from clause and then find the total count. But I was wondering why Postgres does not allow such expression?
It's unclear to me, what result you are after. But in general, you need to move the unnest to the FROM clause to do anything "regular" with the values
If you want to count per value extracted you can use:
select u.val, count(*)
from table_name t
cross join unnest(regexp_matches(t.column_name, regex)) as u(val)
group by u.val;
Or maybe you want to count per "column_name"?
select t.column_name, count(*)
from table_name t
cross join unnest(regexp_matches(t.column_name, regex)) as u(val)
group by t.column_name;

How to use DISTINCT with string_agg() and to_timestamp()?

I want comma separated unique from_date in one row.
So I am using distinct() function in TO_TIMESTAMP() but getting errors.
SELECT string_agg(TO_CHAR(TO_TIMESTAMP(distinct(from_date) / 1000), 'DD-MM-YYYY'), ',')
FROM trn_day_bookkeeping_income_expense
GROUP BY from_date,enterprise_id having enterprise_id = 5134650;
I want output like:
01-10-2017,01-11-2017,01-12-2017
But I am getting errors like:
ERROR: DISTINCT specified, but to_timestamp is not an aggregate function
LINE 1: SELECT string_agg(TO_CHAR(TO_TIMESTAMP(distinct(from_date) /...**
DISTINCT is neither a function nor an operator but an SQL construct or syntax element. Can be added as leading keyword to the whole SELECT list or within most aggregate functions.
Add it to the SELECT list (consisting of a single column in your case) in a subselect where you can also cheaply add ORDER BY. Should yield best performance:
SELECT string_agg(to_char(the_date, 'DD-MM-YYYY'), ',') AS the_dates
FROM (
SELECT DISTINCT to_timestamp(from_date / 1000)::date AS the_date
FROM trn_day_bookkeeping_income_expense
WHERE enterprise_id = 5134650
ORDER BY the_date -- assuming this is the order you want
) sub;
First generate dates (multiple distinct values may result in the same date!).
Then the DISTINCT step (or GROUP BY).
(While being at it, optionally add ORDER BY.)
Finally aggregate.
An index on (enterprise_id) or better (enterprise_id, from_date) should greatly improve performance.
Ideally, timestamps are stored as type timestamp to begin with. Or timestamptz. See:
Ignoring time zones altogether in Rails and PostgreSQL
DISTINCT ON is a Postgres-specific extension of standard SQL DISTINCT functionality. See:
Select first row in each GROUP BY group?
Alternatively, you could also add DISTINCT(and ORDER BY) to the aggregate function string_agg() directly:
SELECT string_agg(DISTINCT to_char(to_timestamp(from_date / 1000), 'DD-MM-YYYY'), ',' ORDER BY to_char(to_timestamp(from_date / 1000), 'DD-MM-YYYY')) AS the_dates
FROM trn_day_bookkeeping_income_expense
WHERE enterprise_id = 5134650
But that would be ugly, hard to read and maintain, and more expensive. (Test with EXPLAIN ANALYZE).
distinct is not a function, it's an operator applied to either all columns in the select list, or a parameter to an aggregate function.
you probably want this:
SELECT string_agg(distinct TO_CHAR(TO_TIMESTAMP(from_date / 1000), 'DD-MM-YYYY'), ',')
from trn_day_bookkeeping_income_expense
group by from_date,enterprise_id
having enterprise_id = 5134650

How to Improve Oracle Query Performance when use ListAggregate function

I need to Display fees for every records I have 13000 records.and I need to Display fees 100+300+500=900. SO I used List Aggregate function when i Use List Aggregate I need to give a order by (Syntax) it Affects My Query Performance
My Query is below
SELECT
(SELECT REPLACE(ListAgg(amount,',')within groupby(
ORDER BY Auditcomptranid),',','+')
||'=
||SUM(amount)AS Fees
FROM Audit_comp_trans
WHERE Audit_comp_trans.SSU_ID=SSU_Request.SSU_ID
AND Audit_comp_trans.Aucompid= SSU_Request.Aucompid
)
FROM SSU_Request
INNER JOIN comp_infromation
ORDER BY SSU_ID DESC
I'd suggest to split the task in two steps.
In the first place define the subquery providing the grouping column and the fee column (I use subquery factoring in my example, you must modify it to your source tables).
For the second step, the formatting of the formula you may use the ListAgg function, but you must follow the syntax rules. The example below illustrates the usage:
with tab as (
-- your query here
-- providing the group column and fee column
select group_id, fee ....
)
select group_id,
listagg(fee,' + ') within group (order by fee) ||
' = ' || sum(fee) as fees
from tab
group by group_id

Group by or Distinct - But several fields

How can I use a Distinct or Group by statement on 1 field with a SELECT of All or at least several ones?
Example: Using SQL SERVER!
SELECT id_product,
description_fr,
DiffMAtrice,
id_mark,
id_type,
NbDiffMatrice,
nom_fr,
nouveaute
From C_Product_Tempo
And I want Distinct or Group By nom_fr
JUST GOT THE ANSWER:
select id_product, description_fr, DiffMAtrice, id_mark, id_type, NbDiffMatrice, nom_fr, nouveaute
from (
SELECT rn = row_number() over (partition by [nom_fr] order by id_mark)
, id_product, description_fr, DiffMAtrice, id_mark, id_type, NbDiffMatrice, nom_fr, nouveaute
From C_Product_Tempo
) d
where rn = 1
And this works prfectly!
If I'm understanding you correctly, you just want the first row per nom_fr. If so, you can simply use a subquery to get the lowest id_product per nom_fr, and just get the corresponding rows;
SELECT * FROM C_Product_Tempo WHERE id_product IN (
SELECT MIN(id_product) FROM C_Product_Tempo GROUP BY nom_fr
);
An SQLfiddle to test with.
You need to decide what to do with the other fields. For example, for numeric fields, do you want a sum? Average? Max? Min? For non-numeric fields to you want the values from a particular record if there are more than one with the same nom_fr?
Some SQL Systems allow you to get a "random" record when you do a GROUP BY, but SQL Server will not - you must define the proper aggregation for columns that are not in the GROUP BY.
GROUP BY is used to group in conjunction with an aggregate function (see http://www.w3schools.com/sql/sql_groupby.asp), so it's no use grouping without counting, summing up etc. DISTINCT eleminates duplicates but how that matches with the other columns you want to extract, I can't imagine, because some rows will be removed from the result.

GROUP BY / aggregate function confusion in SQL

I need a bit of help straightening out something, I know it's a very easy easy question but it's something that is slightly confusing me in SQL.
This SQL query throws a 'not a GROUP BY expression' error in Oracle. I understand why, as I know that once I group by an attribute of a tuple, I can no longer access any other attribute.
SELECT *
FROM order_details
GROUP BY order_no
However this one does work
SELECT SUM(order_price)
FROM order_details
GROUP BY order_no
Just to concrete my understanding on this.... Assuming that there are multiple tuples in order_details for each order that is made, once I group the tuples according to order_no, I can still access the order_price attribute for each individual tuple in the group, but only using an aggregate function?
In other words, aggregate functions when used in the SELECT clause are able to drill down into the group to see the 'hidden' attributes, where simply using 'SELECT order_no' will throw an error?
In standard SQL (but not MySQL), when you use GROUP BY, you must list all the result columns that are not aggregates in the GROUP BY clause. So, if order_details has 6 columns, then you must list all 6 columns (by name - you can't use * in the GROUP BY or ORDER BY clauses) in the GROUP BY clause.
You can also do:
SELECT order_no, SUM(order_price)
FROM order_details
GROUP BY order_no;
That will work because all the non-aggregate columns are listed in the GROUP BY clause.
You could do something like:
SELECT order_no, order_price, MAX(order_item)
FROM order_details
GROUP BY order_no, order_price;
This query isn't really meaningful (or most probably isn't meaningful), but it will 'work'. It will list each separate order number and order price combination, and will give the maximum order item (number) associated with that price. If all the items in an order have distinct prices, you'll end up with groups of one row each. OTOH, if there are several items in the order at the same price (say £0.99 each), then it will group those together and return the maximum order item number at that price. (I'm assuming the table has a primary key on (order_no, order_item) where the first item in the order has order_item = 1, the second item is 2, etc.)
The order in which SQL is written is not the same order it is executed.
Normally, you would write SQL like this:
SELECT
FROM
JOIN
WHERE
GROUP BY
HAVING
ORDER BY
Under the hood, SQL is executed like this:
FROM
JOIN
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
Reason why you need to put all the non-aggregate columns in SELECT to the GROUP BY is the top-down behaviour in programming. You cannot call something you have not declared yet.
Read more: https://sqlbolt.com/lesson/select_queries_order_of_execution
SELECT *
FROM order_details
GROUP BY order_no
In the above query you are selecting all the columns because of that its throwing an error not group by something like..
to avoid that you have to mention all the columns whichever in select statement all columns must be in group by clause..
SELECT *
FROM order_details
GROUP BY order_no,order_details,etc
etc it means all the columns from order_details table.
To use group by clause you have to mention all the columns from select statement in to group by clause but not the column from aggregate function.
TO do this instead of group by you can use partition by clause you can use only one port to group as a partition by.
you can also make it as partition by 1
use Common table expression(CTE) to avoid this issue.
multiple CTes also come handy, pasting a case where I have used...maybe helpful
with ranked_cte1 as
( select r.mov_id,DENSE_RANK() over ( order by r.rev_stars desc )as rankked from ratings r ),
ranked_cte2 as ( select * from movie where mov_id=(select mov_id from ranked_cte1 where rankked=7 ) ) select * from ranked_cte2
select * from movie where mov_id=902