How to represent a tree structure with ORDER BY OR GROUP BY - sql

Given a table 'TEMPLATES'(with columns ID,NAME,IS_MASTER,MASTER_ID etc) which describes multiple content management system elements e.g. masters, templates etc.
The column IS_MASTER filters the master-docs. I want to filter all master-docs and the associated elements(documents), which point to the master over the 'MASTER_ID' column of the same table but I want to order the rows one after another for example after a result row which shows a master should be listed the elements, which point to this master (and not all rows mixed up like in the screenshot) :
But I can only do it in this way, I don't know how to order or group by
SELECT x.*,x.ROWID FROM TEMPLATE x
WHERE IS_MASTER IN (1) OR MASTER_ID IS NOT NULL

You could do this:
select *
from templates
order by nvl(master_id, id);
This would tell the database to sort by master_id, and if that column is empty (NULL) to use the id. This way the master and its children are sorted together.
Alternatively you can use a hierarchical query:
select *
from templates
start with master_id is null
connect by master_id = prior id;

Related

Dynamic column in oracle sql

I want to query in database for some ledgername (like child),ledgergroupname (like parent,it's parent of ledgername) and all ascending parent's of ledgergroupname (or ledgername).Data i am searching from ACC_LEDGER table where ledgername and it's immediate parent termed as ledgergroupname are saved.Ascending parent of ledgergroupname are saved in ACC_LEDGERGROUP table.
sql--
select
pp.Ledgercode,
pp.Ledgername,
pp.Ledgergroupcode,
pp.Ledgergroupname,
(select Acc_Ledgergroup.Parentname from Acc_Ledgergroup where Acc_Ledgergroup.Ledgergrpcode=pp.Ledgergroupcode) as PARENTNAME
from
(select
LED.LEDGERCODE,
Led.Ledgername,
Led.Ledgergroupcode,
Led.Ledgergroupname
from ACC_LEDGER LED where Led.Ledgercode IN ('01003024007','01003024019'))pp
it gives me result--
what i want is --
so for every ledger i want to show parent upto root (it's level is different for different ledger means for some ledger it has 7 upper parent's to reach "root" parent).
I am trying to query something like this(it's for your understanding)--
select
pp.Ledgercode,
pp.Ledgername,
pp.Ledgergroupcode,
pp.Ledgergroupname,
(select Acc_Ledgergroup.Parentname from Acc_Ledgergroup where Acc_Ledgergroup.Ledgergrpcode=pp.Ledgergroupcode) as PARENTNAME
while(parentname != root)
{
select parent name from Ledger group
}
from the_table
So column selection from table or join of table is not fixed,it's depend on how much level of parent i have,how can i do that in oracle sql?
Generally this kind of problems can be solved with hierarchical queries (using CONNECT BY), however in Oracle a query cannot have a variable number of columns. As a workaround you can use sys_connect_by_path to concatenate all levels using some separator:
select sys_connect_by_path(Ledgergroupname, '/')
from Acc_Ledgergroup
where Ledgergroupname = 'root'
start with Ledgercode in ('01003024007', '01003024019')
connect by prior Ledgercode = Ledgergroupcode

How to set/serialize values based on results from multiple rows / multiple columns in postgresql

I have a table in which I want to calculate two columns values based on results from multiple rows / multiple columns. The primary key is set on the first two columns (tag,qid).
I would like to set the values of two fields (serial and total).
The "serial" column value is unique for each (tag,qid) so if I have 2 records with same tag, I must have record one with serial# 1 and record two with serial# 2 and so on. The serial must be calculated with accordance to priority field in which higher priority values must start serializing first.
the "total" column is the total number of each tag in the table
I would like to do this in plain SQL instead of creating a stored procedure/cursors, etc...
the table below shows full valid settings.
                                 
 +----+----+--------+-------+-----+  
 |tag |qid |priority|serial |total|  
 +--------------------------------+  
 |abc | 87 |  99    |  1    |  2  |  
 +--------------------------------+  
 |abc | 56 |  11    |  2    |  2  |  
 +--------------------------------+  
 |xyz | 89 |  80    |  1    |  1  |  
 +--------------------------------+  
 |pfm | 28 |  99    |  1    |  3  |  
 +--------------------------------+  
 |pfm | 17 |  89    |  2    |  3  |  
 +--------------------------------+  
 |pfm | 64 |  79    |  3    |  3  |  
 +----+----+--------+-------+-----+  
  
Many Thanks
You can readily return a result set with this information using window functions:
select tag, qid, priority,
row_number() over (partition by tag, qid order by priority desc) as serial,
count(*) over (partition by tag, qid) as total
from table t;

How to search on levelOrder values un SQL?

I have a table in SQL Server that contains the following columns :
Id Name ParentId LevelOrder
8 vehicle 0 0/8/
9 car 8 0/8/9/
10 bike 8 0/8/10/
11 House 0 0/11/
...
This creates a tree.
Say that I have the LevelOrder 0/8/, this should return only the car and bike rows, but how do I handle this in SQL Server?
I have tried :
Select * FROM MyTable WHERE LevelOrder >= '0/8/'
but that does not work.
The underscore character will guarantee at least one character comes after '0/8/', so you don't get a match on the "vehicle" row.
SELECT *
FROM MyTable
WHERE LevelOrder LIKE '0/8/_%'
This code allows you to select values that start with 0/8/
Select * FROM MyTable WHERE LevelOrder like '0/8/%'
Okay -
While #Joe's answer is the simplest and easiest to implement (and possibly better performing than what I'm about to propose...), there are some issues with update anomalies.
Specifically:
You already have a parentId column. You need to synchronize both this and the levelOrder column, or risk inconsistent data. (I believe this also violates 1NF, although my understanding of the exact definition is a little sketchy...)
levelOrder contains the entire heirarchy. If any one parent is moved, all children rows must have levelOrder modified to reflect this (potentially very messy).
In light of this, here's what I recommend:
Drop the levelOrder column, as its existence will (generally) cause problems.
Use a recursive CTE and the parentId column to build the heirarchy dynamically. Either leave the column where it is, or move it to a dedicated relationship table. Moving one parent then requires only one cell to be updated, and cannot result in any (data, not semantic) anomalies. The CTE should look similar to this form (will need to be adjusted for purpose):
WITH heir_parent (parentId, id) as (SELECT parentId, id
FROM table
WHERE id =
UNION ALL
SELECT b.parentId, b.id
FROM heir_parent as a
JOIN table as b
ON b.parentId = a.id)
At the moment, the CTE returns a list of all children of the given id, with their id and their immediate parent. It can be adjusted to return a number of other things as well - although I recommend that the CTE be used only to generate the relationship, and join externally to get the remaining data.

Nested query using while condition - sql

I need to do a nested query on a single table. Each row is potentially the parent or child of another row.
Is it possible to do this with a single select statement? Ive started using this statement but it only goes down one level.
select * from myTable where parent_id in
(select id from myTable where name = 'manager' )
This select however will only go down one level. If a row has multiple children they will be ignored. (In the table each row has an Id field , if a row has a parent then the parents Id value will be in the child's parent_Id field. )
If i could include a while loop in the SQL which would always check to see if the returned Id was a parent or not and if it was check and see if any other row was its child by checking the other rows parent_Id. However i m concerned this would take alot of cycles to eventually find all parent child relationships. Any suggestions? Thanks
using Oracle db
I think you are looking for a hierarchical query like this:
select * from mytable
connect by prior id = parent_id
start with name = 'Manager';
(A "nested table" is something else entirely.)

order by field with more than 10000 ids

I need to do specific ordering with use of order by field.
select * from table order by field(id,3,4,1,2.......upto 10000 ids)
As the ordering required is not gettable from SQL then how much it affect as per performance and is it feasible to do?
Updates from the comments:
Ordering depends on user and category IDs and can be anything the user wants.
The ordering specification changes (about) daily.
So, we need a custom ordering that depends on the user and category and this ordering needs to change daily.
The easiest way would be to put your ordering in a separate table (called ordering_table in this example):
id | position
----+----------
1 | 11
2 | 42
3 | 23
etc.
The above would mean "put an id of 1 at position 11, 2 at position 42, 3 at position 23, ...". Then you can join that ordering table in:
SELECT t.id, t.col1, t.col2
FROM some_table t
JOIN ordering_table o ON (t.id = o.id)
ORDER BY o.position
Where ordering_table is the table (as above) that defines your strange ordering. This approach simply represents your ordering function as a table (any function with a finite domain is, essentially, just a table after all).
This "ordering table" approach should work fine as long as the ordering table is complete.
If you only need this strange ordering in one place then you could merge the position column into your main table and add NOT NULL and UNIQUE constraints on that column to make sure you cover everything and have a consistent ordering.
Further commenting indicates that you want different orderings for different users and categories and that the ordering will change on a daily basis. You could make separate tables for each condition (which would lead to a combinatorial explosion) or, as Mikael Eriksson and ypercube suggest, add a couple more columns to the ordering table to hold the user and category:
CREATE TABLE ordering_table (
thing_id INT NOT NULL,
position INT NOT NULL,
user_id INT NOT NULL,
category_id INT NOT NULL
);
The thing_id, user_id, and category_id would be foreign keys to their respective tables and you'd probably want to index all the columns in ordering_table but a couple minutes of looking at the query plans would be worthwhile to see if the indexes get used would be worthwhile. You could also make all four columns the primary key to avoid duplicates. Then, the lookup query would be something like this:
SELECT t.id, t.col1, t.col2
FROM some_table t
LEFT JOIN ordering_table o
ON (t.id = o.thing_id AND o.user_id = $user AND o.category_id = $cat)
ORDER BY COALESCE(o.position, 99999)
Where $user and $cat are the user and category IDs (respectively). Note the change to a LEFT JOIN and the addition of COALESCE to allow for missing rows in ordering_table, these changes will push anything that doesn't have a specified position in the order to the bottom of the list rather than removing them from the results completely.