sort only particular rows sql server - linq

I have six rows like
col1 col2
--------------
Apple 120
XApple 140
Banana 130
Xbanana 150
Car 110
XCar 160
I would like to sort these rows on col2 but leave the rows with 'X' alone.
so after sorting the rows should be like
col1 col2
--------------
Car 110
Apple 120
Banana 130
XCar 160
XApple 140
Xbanana 150
meaning, the rows with car apple and banana should be sorted but the rows with xcar, xapple and xbanana should be left alone and just be appended at the end.
I tried
select *
from table
where col1 not like 'X%' order by col2
union
select *
from table
where symbol like 'X%'
but sql server doesn't allow that. Could anybody point me to the right direction or tell me that this is not possible?
PS: any LINQ solution will also be fine.
thanks

Order by whether the first character of col1 is 'X' or not, and then by col2.
Example:
SELECT *
FROM table
ORDER BY CASE WHEN col1 LIKE 'X%' THEN 1 ELSE 0 END,col2
Although, this doesn't leave the LIKE 'X%' rows unordered, neither did your example.

There is no order without an explicit ORDER BY clause. While you can use UNION to group the rows, you cannot guarantee that the order of the unordered rows is stable. See here.
The following will split the list into two groups of rows, but each will be sorted by Col1:
select Col1, Col2
from (
select Col1, Col2, 1 as PleaseSort
from MyTable
where Col1 not like 'X%'
union
select Col1, Col2, 0
from MyTable
where Col1 like 'X%' ) as PlaceHolder
order by PleaseSort desc, Col1

If you know the sort of upper limit for how many x rows you'll get then you could do something like....
select * from (select top 5000 col1 from #tmp order by col1) a
union
select col1 from #tmp

Related

snowflake select max date from date array

Imagine I have a table with some field one of which is array off date.
as below
col1 col2 alldate Max_date
1 2 ["2021-02-12","2021-02-13"] "2021-02-13"
2 3 ["2021-01-12","2021-02-13"] "2021-02-13"
4 4 ["2021-01-12"] "2021-01-12"
5 3 ["2021-01-11","2021-02-12"] "2021-02-12"
6 7 ["2021-02-13"] "2021-02-13"
I need to write a query such that to select only the one which has max date in there array. And there is a column which has max date as well.
Like the select statement should show
col1 col2 alldate Max_date
1 2 ["2021-02-12","2021-02-13"] "2021-02-13"
2 3 ["2021-01-12","2021-02-13"] "2021-02-13"
6 7 ["2021-02-13"] "2021-02-13"
The table is huge so a optimized query is needed.
Till now I was thinking of
select col1, col2, maxdate
from t1 where array_contains((select max(max_date) from t1)::variant,date));
But to me it seems running select statement per query is a bad idea.
Any Suggestion
If you want pure speed using lateral flatten is 10% faster than the array_contains approach over 500,000,000 records on a XS warehouse. You can copy paste below code straight into snowflake to test for yourself.
Why is the lateral flattern approach faster?
Well if you look at the query plans the optimiser filters at first step (immediately culling records) where as the array_contains waits until the 4th step before doing the same. The filter is the qualifier of the max(max_date) ...
Create Random Dataset:
create or replace table stack_overflow_68132958 as
SELECT
seq4() col_1,
UNIFORM (1, 500, random()) col_2,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_1,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_2,
DATEADD(day, UNIFORM (-40, -0, random()), current_date()) random_date_3,
ARRAY_CONSTRUCT(random_date_1, random_date_2, random_date_3) date_array,
greatest(random_date_1, random_date_2, random_date_3) max_date,
to_array(greatest(random_date_1, random_date_2, random_date_3)) max_date_array
FROM
TABLE (GENERATOR (ROWCOUNT => 500000000)) ;
Test Felipe/Mike approach -> 51secs
select
distinct
col_1
,col_2
from
stack_overflow_68132958
qualify
array_contains(max(max_date) over () :: variant, date_array);
Test Adrian approach -> 47 secs
select
distinct
col_1
, col_2
from
stack_overflow_68132958
, lateral flatten(input => date_array) g
qualify
max(max_date) over () = g.value;
I would likely use a CTE for this, like:
WITH x AS (
SELECT max(max_date) as max_max_date
FROM t1
)
select col1, col2, maxdate
from t1
cross join x
where array_contains(x.max_max_date::variant,alldate);
I have not tested the syntax exactly and the data types might vary things a bit, but the concept here is that the CTE will be VERY fast and return a single record with a single value. A MAX() function leverage metadata in Snowflake, so it won't even use a warehouse to get it.
That said, the Snowflake profiler is pretty smart, so your query might actually create the exact same query profile as this statement. Test them both and see what the Profile looks like to see if it truly makes a difference.
To build on Mike's answer, we can do everything in the QUALIFY, without the need for a CTE:
with t1 as (
select 'a' col1, 'b' col2, '2020-01-01'::date maxdate, array_construct('2020-01-01'::date, '2018-01-01', '2017-01-01') alldate
)
select col1, col2, alldate, maxdate
from t1
qualify array_contains((max(maxdate) over())::variant, alldate)
;
Note that you should be careful with types. Both of these are true:
select array_contains('2020-01-01'::date::string::variant, array_construct('2020-01-01', '2019-01-01'));
select array_contains('2020-01-01'::date::variant, array_construct('2020-01-01'::date, '2019-01-01'));
But this is false:
select array_contains('2020-01-01'::date::variant, array_construct('2020-01-01', '2019-01-01'));
You have some great answers, which I only saw, after i wrote mine up.
If your data types, match, you should be good to go, copy paste direct into snowflake ... and this should work.
create or replace schema abc;
use schema abc;
create or replace table myarraytable(col1 number, col2 number, alldates variant, max_date timestamp_ltz);
insert into myarraytable
select 1,2,array_construct('2021-02-12'::timestamp_ltz,'2021-02-13'::timestamp_ltz), '2021-02-13'
union
select 2,3,array_construct('2021-01-12'::timestamp_ltz,'2021-02-13'::timestamp_ltz),'2021-02-13'
union
select 4,4,array_construct('2021-01-12'::timestamp_ltz) , '2021-01-12'
union
select 5,3,array_construct('2021-01-11'::timestamp_ltz,'2021-02-12'::timestamp_ltz) , '2021-02-12'
union
select 6,7,array_construct('2021-02-13'::timestamp_ltz) , '2021-02-13';
select * from myarraytable
order by 1 ;
WITH cte_max AS (
SELECT max(max_date) as max_date
FROM myarraytable
)
select myarraytable.*
from myarraytable, cte_max
where array_contains(cte_max.max_date::variant, alldates)
order by 1 ;

Oracle Select unique on multiple column

How can I achieve this to Select to one row only dynamically since
the objective is to get the uniqueness even on multiple columns
select distinct
coalesce(least(ColA, ColB),cola,colb) A1, greatest(ColA, ColB) B1
from T
The best solution is to use UNION
select colA from your_table
union
select colB from your_table;
Update:
If you want to find the duplicate then use the EXISTS as follows:
SELECT COLA, COLB FROM YOUR_TABLE T1
WHERE EXISTS (SELECT 1 FROM YOUR_tABLE T2
WHERE T2.COLA = T1.COLB OR T2.COLB = T1.COLA)
If I correctly understand words: objective is to get the uniqueness even on multiple columns, number of columns may vary, table can contain 2, 3 or more columns.
In this case you have several options, for example you can unpivot values, sort, pivot and take unique values. The exact code depends on Oracle version.
Second option is listagg(), but it has limited length and you should use separators not appearing in values.
Another option is to compare data as collections. Here I used dbms_debug_vc2coll which is simple table of varchars. Multiset except does main job:
with t as (select rownum rn, col1, col2, col3,
sys.dbms_debug_vc2coll(col1, col2, col3) as coll
from test )
select col1, col2, col3 from t a
where not exists (
select 1 from t b where b.rn < a.rn and a.coll multiset except b.coll is empty )
dbfiddle with 3-column table, nulls and different test cases

Fetch name based on comma-separated ids

I have two tables, customers and products.
products:
productid name
1 pro1
2 pro2
3 pro3
customers:
id name productid
1 cust1 1,2
2 cust2 1,3
3 cust3
i want following result in select statement,
id name productid
1 cust1 pro1,pro2
2 cust2 pro1,pro3
3 cust3
i have 300+ records in both tables, i am beginner to back end coding, any help?
Definitely a poor database design but the bad thing is that you have to live with that. Here is a solution which I created using recursive query. I don't see the use of product table though since your requirement has nothing to do with product table.
with
--Expanding each row seperated by comma
tab(col1,col2,col3) as (
Select distinct c.id,c.prdname,regexp_substr(c.productid,'[^,]',1,level)
from customers c
connect by regexp_substr(c.productid,'[^,]',1,level) is not null
order by 1),
--Appending `Pro` to each value
tab_final as ( Select col1,col2, case when col3 is not null
then 'pro'||col3
else col3
end col3
from tab )
--Displaying result as expected
SELECT
col1,
col2,
LISTAGG(col3,',') WITHIN GROUP( ORDER BY col1,col2 ) col3
FROM
tab_final
GROUP BY
col1,
col2
Demo:
--Preparing dataset
With
customers(id,prdname,productid) as ( Select 1, 'cust1', '1,2' from dual
UNION ALL
Select 2, 'cust2','1,3' from dual
UNION ALL
Select 3, 'cust3','' from dual),
--Expanding each row seperated by comma
tab(col1,col2,col3) as (
Select distinct c.id,c.prdname,regexp_substr(c.productid,'[^,]',1,level)
from customers c
connect by regexp_substr(c.productid,'[^,]',1,level) is not null
order by 1),
--Appending `Pro` to each value
tab_final as ( Select col1,col2, case when col3 is not null
then 'pro'||col3
else col3
end col3
from tab )
--Displaying result as expected
SELECT
col1,
col2,
LISTAGG(col3,',') WITHIN GROUP( ORDER BY col1,col2 ) col3
FROM
tab_final
GROUP BY
col1,
col2
PS: While using don't forget to put your actual table columns as in my example it may vary.

fetch different columns from two tables and combine the results

I want to combine the Two different tables result in oracle.
conditions:
From both the tables ID ,STAMp columns are common columns.
Remaining columns are different.
Table1;
Element_ID STAMP Col1 Col2
1 22/03/2014 85 100
2 22/03/2014 95 105
Table2.
Element_ID STAMP Col3 Col4
5 22/03/2014 100 110
6 22/04/2014 200 210
Result:
Element_ID STAMP Col1 Col3
1 22/03/2014 85 null
5 22/03/2014 null 100
Query:
Select Element_ID, Stamp, Col1 from Table1 where element_ID in (1, 5)
Select Element_ID, STAMP, Col3 from Table2 where Element_ID in (1,5)
How to combine the above two queries results, and results should come as above format.
You can try as this,
Select Element_ID, Stamp, Col1, NULL Col3 from Table1 where element_ID in (1,5)
union
Select Element_ID, STAMP, NULL Col1, Col3 from Table2 where Element_ID in (1,5)
Select T1.Element_ID,T1.Stamp, T1.Col1, T2.Col3
from Table1 T1, Table2 T2
where T1.Element_ID in (1, 5)
This is one interesting use case for a RIGHT JOIN on some minimum set -- both to restrict the result to records 1 and 5 and to force the presence of those two records:
SELECT "Element_ID", "STAMP", "Col1", "Col3"
FROM Table1
FULL OUTER JOIN Table2
USING("Element_ID", "STAMP")
RIGHT JOIN
(
SELECT 1 "Element_ID" FROM DUAL
UNION ALL SELECT 5 FROM DUAL
) MinimumSet
USING("Element_ID")
See http://sqlfiddle.com/#!4/725d9/18 for a live example

linq create average from different columns

I am having a simple table with 5 columns
id
id_user
col1
col2
col3
how can make Linq query to select an id_user and sum up all the col1, col2, col3 integers to make an average of those 3 columns ?
Thanks
Assuming you already have your table something along these lines should do it:
from row in table
let avgTotal = (new [] {row.col1, row.col2, row.col3}).Average()
select new {row.user_id, avgTotal}

Resources