Custom Sort Based On Referenced Records - sql

Please consider these data:
Id F1 F2 Ref_ID
-------------------------------------------
1 Nima 35 Null
2 Eli 33 Null
3 Arian 5 1
4 Ava 1 1
5 Arsha 3 2
6 Rozhan 30 1
7 Zhina 20 2
I want to sort this table like this result:
Id F1 F2 Ref_ID
-------------------------------------------
1 Nima 35 Null
3 Arian 5 1
4 Ava 1 1
6 Rozhan 30 1
2 Eli 33 Null
5 Arsha 3 2
7 Zhina 20 2
the refrenced records should place under the reference record based on Id ascending.
How I can do this using LINQ or SQL. Thanks

In SQL you could sort it by using a COALESCE or ISNULL for the Ref_id and the Id.
And an IIF or a CASE WHEN to make sure the parent id comes first in the same group of Ref_id.
SELECT Id, F1, F2, Ref_ID
FROM YourTable
ORDER BY COALESCE(Ref_ID, Id), IIF(Ref_ID IS NULL, 0, 1), Id;
A test on db<>fiddle here

Here is a more simple solution:
SELECT
Id,
F1,
F2,
Ref_ID
FROM
#Table
ORDER BY
ISNULL(Ref_ID,ID), ID
Result:

Using LINQ, you can do it like this:
from i in data
orderby i.Ref_ID ?? i.Id
select i;

Another solution is to add an extra column in the query, and sort on that column
select t.*
from ( select Id,
F1,
F2,
Ref_ID,
coalesce(Ref_ID, Id) as RefID_or_Id,
iif(Ref_ID is null, 0, 1) as Ref_ID_0_or_1
from YourTable
) t
order by t.RefID_or_Id,
t.Ref_ID_0_or_1,
t.Id
In case your table is large, you should test wich of the solutions here performs the best for you

Related

SUM a column in SQL, based on DISTINCT values in another column, GROUP BY a third column

I'd appreciate some help on the following SQL problem:
I have a table of 3 columns:
ID Group Value
1 1 5
1 1 5
1 2 10
1 2 10
1 3 20
2 1 5
2 1 5
2 1 5
2 2 10
2 2 10
3 1 5
3 2 10
3 2 10
3 2 10
3 4 50
I need to group by ID, and I would like to SUM the values based on DISTINCT values in Group. So the value for a group is only accounted for once even though it may appear multiple for times for a particular ID.
So for IDs 1, 2 and 3, it should return 35, 15 and 65, respectively.
ID SUM
1 35
2 15
3 65
Note that each Group doesn't necessarily have a unique value
Thanks
the CTE will remove all duplicates, so if there a sdiffrenet values for ID and Group, it will be counted.
The next SELECT wil "GROUP By" ID
For Pstgres you would get
WITH CTE as
(SELECT DISTINCT "ID", "Group", "Value" FROM tablA
)
SELECT "ID", SUM("Value") FROM CTE GROUP BY "ID"
ORDER BY "ID"
ID | sum
-: | --:
1 | 35
2 | 15
3 | 65
db<>fiddle here
Given what we know at the moment this is what I'm thinking...
The CTE/Inline view eliminate duplicates before the sum occurs.
WITH CTE AS (SELECT DISTINCT ID, Group, Value FROM TableName)
SELECT ID, Sum(Value)
FROM CTE
GROUP BY ID
or
SELECT ID, Sum(Value)
FROM (SELECT DISTINCT * FROM TableName) CTE
GROUP BY ID

How to produce a circular shift with postgreSQL

Let's suppose that I have this simple table:
ID
1
2
3
4
If I do:
WITH example AS
(
SELECT UNNEST(ARRAY[1,2,3,4]) as ID
)
SELECT ID, lAG(ID,1) over() as LAG_ID
FROM example
-- The shift is 1 in this case, but could be any integer in practice.
I got:
ID
LAG_ID
1
2
1
3
2
4
3
But I need a circular shift:
ID
LAG_ID
1
4
2
1
3
2
4
3
Is there an elegant way to produce this result ?
If ID is not nullable
WITH example AS
(
SELECT UNNEST(ARRAY[1,2,3,4]) as ID
)
SELECT ID, coalesce(lAG(ID,1) over(order by ID asc), (select max(ID) from example)) as LAG_ID
FROM example

Delete rows, which are duplicated and follow each other consequently

It's hard to formulate, so i'll just show an example and you are welcome to edit my question and title.
Suppose, i have a table
flag id value datetime
0 b 1 343 13
1 a 1 23 12
2 b 1 21 11
3 b 1 32 10
4 c 2 43 11
5 d 2 43 10
6 d 2 32 9
7 c 2 1 8
For each id i want to squeze the table by flag columns such that all duplicate flag values that follow each other collapse to one row with sum aggregation. Desired result:
flag id value
0 b 1 343
1 a 1 23
2 b 1 53
3 c 2 75
4 d 2 32
5 c 2 1
P.S: I found functions like CONDITIONAL_CHANGE_EVENT, which seem to be able to do that, but the examples of them in docs dont work for me
Use the differnece of row number approach to assign groups based on consecutive row flags being the same. Thereafter use a running sum.
select distinct id,flag,sum(value) over(partition by id,grp) as finalvalue
from (
select t.*,row_number() over(partition by id order by datetime)-row_number() over(partition by id,flag order by datetime) as grp
from tbl t
) t
Here's an approach which uses CONDITIONAL_CHANGE_EVENT:
select
flag,
id,
sum(value) value
from (
select
conditional_change_event(flag) over (order by datetime desc) part,
flag,
id,
value
from so
) t
group by part, flag, id
order by part;
The result is different from your desired result stated in the question because of order by datetime. Adding a separate column for the row number and sorting on that gives the correct result.

Select unique subsets

I have a table like in example below.
SQL> select * from test;
ID PARENT_ID NAME
1 1 A
2 1 B
3 2 A
4 2 B
5 3 A
6 3 B
7 3 C
8 4 A
What I need is to get all unique subsets of names ((A,B), (A,B,C), (A)) or exclude duplicate subsets. You can see that (A,B) is twice there, one for PARENT_ID=1 and one for 2.
I want to exclude such duplicates:
ID PARENT_ID NAME
1 1 A
2 1 B
5 3 A
6 3 B
7 3 C
8 4 A
You can use DISTINCT to only return different values.
e.g.
SELECT DISTINCT GROUP_CONCAT(NAME SEPARATOR ',') as subsets
FROM TABLE_1
GROUP BY PARENT_ID;
SQL Fiddle
I have used 'group_concat' assuming you are using 'Mysql'. The equivalent function in Oracle is 'listagg()'. you can see it in action here in SQL fiddle
Here is the solution:-
Select a.* from
test a
inner join
(
Select nm, min(parent_id) as p_id
from
(
Select Parent_id, group_concat(NAME) as nm
from test
group by Parent_ID
) a
group by nm
)b
on a.Parent_id=b.p_id
order by parent_id, name

Extracting sub data from a table

I would be happy for your help.
I have a table like this :
[MS_CODE] [MS_SML]
1 43
1 AA
2 51
3 24
3 21
4 11
4 43
5 AA
6 11
I want to write a query that will serach for the [MS_SML] which shows up in group (1 or 2 or 3) And (4 or 5 or 6) in [MS_Code].
For example:
43,AA because 43 is in a row where ms_code is 1 and 4 and same for 'AA'. I would like to create output like this:
[MS_Code] [MS_SML]
1 43
4 43
1 AA
5 AA
Thank you very much for your help!
One method is to use exists and apply your criteria:
select t.*
from t
where exists (select 1
from t t2
where t2.ms_sml = t1.ms_sml and t2.ms_code in (1, 2, 3)
) and
exists (select 1
from t t2
where t2.ms_sml = t1.ms_sml and t2.ms_code in (4, 5, 6)
);
Here is one way to do it.
select ms_code, ms_sml
from msc
where ms_sml in
(
select ms_sml
from msc
where ms_code in (1,2,3)
intersect
select ms_sml
from msc
where ms_code in (4,5,6)
)
order by ms_sml, ms_code
Note: If there is more than one ms_code for a given ms_sml in the same group, this will return all of them.
Suppose AA is mapped to 1, 3 and 5, this will return
1 AA
3 AA
5 AA
If that is an issue, We may need additional logic to deal with that: for example pick the minimum value of ms_code within the group.