use distinct and order by in STRING_AGG function - sql

I am trying the string_agg a column while at the same time ordering the column and only show unique values. Consider the following demo. IS there a syntax issue or is this simply not possible with the method I am using?
SELECT STRING_AGG(DISTINCT foo.a::TEXT,',' ORDER BY foo.a DESC)
FROM (
SELECT 1 As a
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 2
) AS foo
[2019-11-22 13:29:32] [42P10] ERROR: in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list
[2019-11-22 13:29:32] Position: 53

The error message is quite clear. The expression that you use in the ORDER BY clause must also appear in the aggregated part.
You could do:
SELECT STRING_AGG(DISTINCT foo.a::TEXT, ',' ORDER BY foo.a::TEXT DESC)
FROM (
SELECT 1 As a
UNION ALL SELECT 1
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS foo
Demo on DB Fiddle
While this will work, the problem with this solution is that it will order numbers as strings, that do not have the same ordering rules. String wise, 10 is less than 2.
Another option is to use arrays: first, ARRAY_AGG() can be used to aggregate the numbers (with proper, numeric ordering), then you can turn it to a comma-separated list of strings with ARRAY_TO_STRING().
SELECT ARRAY_TO_STRING(ARRAY_AGG(DISTINCT a ORDER BY a DESC), ',')
FROM (
SELECT 1 As a
UNION ALL SELECT 1
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS foo
Demo on DB Fiddle

Related

how to show null value at the end when using union all in sql

I am using this code to list numbers ASC and null values listing after the value in ms sql:
ORDER BY -ur.sira_no DESC
But if i use two select query with "UNION ALL" and than my query is not working.
Error:
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
How can we order null values after the numbers using UNION ALL?
Use a subquery:
select x.*
from ((select . . .
) union all
(select . . .
)
) x
ORDER BY - x.sira_no DESC;
However, I would recommend being more explicit:
ORDER BY (CASE WHEN sira_no IS NOT NULL THEN 1 ELSE 2 END),
sira_no ASC
in your original query. SQL Server allows you to order by column names in a union all query, but not on expressions.

Big Query - String Function

I am very new in BigQuery platform, i want to take the following strings
SOCKETIOEXCEPTION##APS.COM, NULLPOINTEREXCEPTION##RSJAVA.COM, CLASSCASTEEXCEPTION##MPS.COM
And get this as a result: SOCKETIOEXCEPTION, NULLPOINTEREXCEPTION, CLASSCASTEEXCEPTION
Before ## characters I want to separate from a given string and then I want to group by number rows available in the above-mentioned tag like SOCKETIOEXCEPTION, NULLPOINTEREXCEPTION, CLASSCASTEEXCEPTION
Sample db details
How do I write this query?
Below is for BigQuery Standard SQL
#standardSQL
SELECT SPLIT(line, '##')[OFFSET(0)] type, COUNT(1) cnt
FROM `project.dataset.table`
GROUP BY type
You can test, play with above using sample data from your question as in example below
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'SOCKETIOEXCEPTION##111' line UNION ALL
SELECT 'SOCKETIOEXCEPTION##222' UNION ALL
SELECT 'SOCKETIOEXCEPTION##333' UNION ALL
SELECT 'NULLPOINTEREXCEPTION##444' UNION ALL
SELECT 'NULLPOINTEREXCEPTION##555' UNION ALL
SELECT 'CLASSCASTEEXCEPTION##666' UNION ALL
SELECT 'CLASSCASTEEXCEPTION##777' UNION ALL
SELECT 'CLASSCASTEEXCEPTION##888'
)
SELECT SPLIT(line, '##')[OFFSET(0)] type, COUNT(1) cnt
FROM `project.dataset.table`
GROUP BY type
with result
Row type cnt
1 SOCKETIOEXCEPTION 3
2 NULLPOINTEREXCEPTION 2
3 CLASSCASTEEXCEPTION 3

Invalid Column Name in union clause

I am trying to add a fixed value 'ALLE' to my column with a union clause, but I get an error:
Invalid column name BoltPattern
at my order by clause
How exactly do I do this?
SELECT 'ALLE'
UNION
SELECT BoltPattern
FROM [OminiTire].[Data].[WheelData]
WHERE BoltPattern IS NOT NULL
GROUP BY BoltPattern
ORDER BY
CASE
WHEN BoltPattern = 'ALLE'
THEN 1
ELSE 2
END, BoltPattern
I recommend using a subquery . . . and union all:
SELECT BoltPattern
FROM ((SELECT 'ALLE' as BoltPattern, 1 as ord)
UNION ALL
(SELECT DISTINCT BoltPattern, 2 as ord
FROM [OminiTire].[Data].[WheelData]
WHERE BoltPattern is not null
)
) x
ORDER BY ord, BoltPattern;
Notes:
UNION incurs overhead to remove duplicates. UNION ALL does not.
I find SELECT DISTINCT to be more succinct than using the GROUP BY.
The subquery allows you to define the explicit ordering -- easier to maintain (in my opinion) than a complicated CASE expression.

Aggregate two columns and rows into one

I have the following table structure
start|end
09:00|11:00
13:00|14:00
I know
SELECT ARRAY_AGG(start), ARRAY_AGG(end)
Will result in
start|end
[09:00,13:00]|[11:00,14:00]
But how can i get the following result?
result
[09:00,11:00,13:00,14:00]
BTW, I'm using Postgres
You could do array concatenation (if order is not important):
SELECT ARRAY_AGG(start) || ARRAY_AGG(end) FROM TABLE1
If order is important you could use Gordon's approach but:
add aggregate order array_agg(d order by d ASC)
use unnest instead of union all, because Gordon's solution (union all) performs two sequence scan. If table is big it could be better for performance to use:
SELECT array_agg(d ORDER BY d ASC) FROM(
SELECT unnest(ARRAY[start] || ARRAY[end]) as d from table1
) sub
which performs only one sequence scan on table (and will be faster).
One method is to unpivot them and then aggregate:
select array_agg(d)
from (select start as d from t
union all
select end as d from t
) t;
A similar method uses a cross join:
select array_agg(case when n.n = 1 then t.start else t.end end)
from t cross join
(select 1 as n union all select 2) n;
I assume the start and end are character type
select ARRAY_AGG(col)
from(select string_agg(strt::text||','||en::text,',') col
from b
)t

How to execute UNION without sorting? (SQL)

UNION joins two results and remove duplicates, while UNION ALL does not remove duplicates.
UNION also sort the final output.
What I want is the UNION ALL without duplicates and without the sort. Is that possible?
The reason for this is that I want the result of the first query to be on top of the final result, and the second query at the bottom (and each sorted as if they where run individually).
I notice this question gets quite a lot of views so I'll first address a question you didn't ask!
Regarding the title. To achieve a "Sql Union All with “distinct”" then simply replace UNION ALL with UNION. This has the effect of removing duplicates.
For your specific question, given the clarification "The first query should have "priority", so duplicates should be removed from bottom" you can use
SELECT col1,
col2,
MIN(grp) AS source_group
FROM (SELECT 1 AS grp,
col1,
col2
FROM t1
UNION ALL
SELECT 2 AS grp,
col1,
col2
FROM t2) AS t
GROUP BY col1,
col2
ORDER BY MIN(grp),
col1
"UNION also sort the final output" - only as an implementation artifact. It is by no means guaranteed to perform the sort, and if you need a particular sort order, you should specify it with an ORDER BY clause. Otherwise, the output order is whatever is most convenient for the server to provide.
As such, your request for a function that performs a UNION ALL but that removes duplicates is easy - it's called UNION.
From your clarification, you also appear to believe that a UNION ALL will return all of the results from the first query before the results of the subsequent queries. This is also not guaranteed. Again, the only way to achieve a particular order is to specify it using an ORDER BY clause.
SELECT *, 1 AS sort_order
FROM table1
EXCEPT
SELECT *, 1 AS sort_order
FROM table2
UNION
SELECT *, 1 AS sort_order
FROM table1
INTERSECT
SELECT *, 1 AS sort_order
FROM table2
UNION
SELECT *, 2 AS sort_order
FROM table2
EXCEPT
SELECT *, 2 AS sort_order
FROM table1
ORDER BY sort_order;
But the real answer is: other than the ORDER BY clause, the sort order will by arbitrary and not guaranteed.
Consider these tables (Standard SQL code, runs on SQL Server 2008):
WITH A
AS
(
SELECT *
FROM (
VALUES (1),
(2),
(3),
(4),
(5),
(6)
) AS T (col)
),
B
AS
(
SELECT *
FROM (
VALUES (9),
(8),
(7),
(6),
(5),
(4)
) AS T (col)
), ...
The desired effect is this to sort table A by col ascending, sort table B by col descending then unioning the two, removing duplicates, retaining order before the union and leaving table A results on the "top" with table B on the "bottom" e.g. (pesudo code)
(
SELECT *
FROM A
ORDER
BY col
)
UNION
(
SELECT *
FROM B
ORDER
BY col DESC
);
Of course, this won't work in SQL because there can only be one ORDER BY clause and it can only be applied to the top level table expression (or whatever the output of a SELECT query is known as; I call it the "resultset").
The first thing to address is the intersection between the two tables, in this case the values 4, 5 and 6. How the intersection should be sorted needs to be specified in SQL code, therefore it is desirable that the designer specifies this too! (i.e. the person asking the question, in this case).
The implication in this case would seem to be that the intersection ("duplicates") should be sorted within the results for table A. Therefore, the sorted resultset should look like this:
VALUES (1), -- A including intersection, ascending
(2), -- A including intersection, ascending
(3), -- A including intersection, ascending
(4), -- A including intersection, ascending
(5), -- A including intersection, ascending
(6), -- A including intersection, ascending
(9), -- B only, descending
(8), -- B only, descending
(7), -- B only, descending
Note in SQL "top" and "bottom" has no inferent meaning and a table (other than a resultset) has no inherent ordering. Also (to cut a long story short) consider that UNION removes duplicate rows by implication and must be applied before ORDER BY. The conclusion has to be that each table's sort order must be explicitly defined by exposing a sort order column(s) before being unioned. For this we can use the ROW_NUMBER() windowed function e.g.
...
A_ranked
AS
(
SELECT col,
ROW_NUMBER() OVER (ORDER BY col) AS sort_order_1
FROM A -- include the intersection
),
B_ranked
AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY col DESC) AS sort_order_1
FROM B
WHERE NOT EXISTS ( -- exclude the intersection
SELECT *
FROM A
WHERE A.col = B.col
)
)
SELECT *, 1 AS sort_order_0
FROM A_ranked
UNION
SELECT *, 2 AS sort_order_0
FROM B_ranked
ORDER BY sort_order_0, sort_order_1;
select T.Col1, T.Col2, T.Sort
from
(
select T.Col1,
T.Col2,
T.Sort,
rank() over(partition by T.Col1, T.Col2 order by T.Sort) as rn
from
(
select Col1, Col2, 1 as Sort
from Table1
union all
select Col1, Col2, 2
from Table2
) as T
) as T
where T.rn = 1
order by T.Sort
Try this:
SELECT DISTINCT * FROM (
SELECT column1, column2 FROM Table1
UNION ALL
SELECT column1, column2 FROM Table2
UNION ALL
SELECT column1, column2 FROM Table3
) X ORDER BY Column1
The sort is used to eliminate the duplicates, and is implicit for DISTINCT and UNION queries (but not UNION ALL) - you could still specify the columns you'd prefer to order by if you need them sorted by specific columns.
For example, if you wanted to sort by the result sets, you could introduce an additional column, and sort by that first:
SELECT foo, bar, 1 as ResultSet
FROM Foo
WHERE bar = 1
UNION
SELECT foo, bar, 2 as ResultSet
FROM Foo
WHERE bar = 3
UNION
SELECT foo, bar, 3 as ResultSet
FROM Foo
WHERE bar = 2
ORDER BY ResultSet
I assume your tables are table1 and table2 respectively,
and your solution is;
(select * from table1 MINUS select * from table2)
UNION ALL
(select * from table2 MINUS select * from table1)
1,1:
select 1 from dual
union all select 1 from dual
1:
select 1 from dual
union select 1 from dual
You can do something like this.
Select distinct name from (SELECT r.name FROM outsider_role_mapping orm1
union all
SELECT r.name FROM user_role_mapping orm2
) tmp;