Sorting Table in hierarchical order - sql-server

Is it possible to sorting queries table in hierarchical order like this:
Expected
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| ID | Code | Name | Qty | Amount | is_parent | parent_id | remarks |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 1 | ABC | Parent1 | 2 | 1,000 | 1 | 0 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 4 | FFLK | Product Z | 10 | 2,500 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 5 | P6DT | Product 5 | 7 | 1,700 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 6 | P2GL | Product T | 5 | 1,100 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 2 | DHG | Parent2 | 5 | 1,500 | 1 | 0 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 3 | LMSJ | Product U | 4 | 600 | 0 | 2 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
This is the original data table:
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| ID | Code | Name | Qty | Amount | is_parent | parent_id | remarks |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 1 | ABC | Parent1 | 2 | 1,000 | 1 | 0 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 2 | DHG | Parent2 | 5 | 1,500 | 1 | 0 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 3 | LMSJ | Product U | 4 | 600 | 0 | 2 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 4 | FFLK | Product Z | 10 | 2,500 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 5 | P6DT | Product 5 | 7 | 1,700 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
| 6 | P2GL | Product T | 5 | 1,100 | 0 | 1 | xxx |
+----+--------+-----------+-------+--------+-----------+-----------+---------+
is_parent column = 1 if data row set to parent, 0 if data row set to child
parent_id column = 0 if data row set to parent, depend on ID of parent data
I'm using SQL Server to generate the data.

It looks like the actual question is how to query the data in hierarchical order. This is possible using recursive queries but a faster alternative is to use SQL Server's support for hierarchical data.
A recursive query that returns the data in hierarchical order would look like this :
WITH h AS
(
SELECT
ID,Code,Name,Qty,Amount,is_parent,parent_id,remarks
FROM
dbo.ThatTable
WHERE
parent_id=0
UNION ALL
SELECT
c.ID,c.Code,c.Name,c.Qty,c.Amount,c.is_parent,c.parent_id,c.remarks
FROM
dbo.ThatTable c
INNER JOIN h ON
c.parent_id= h.Id
)
SELECT * FROM h
This query's performance will be acceptable if the ID and Parent_ID fields are indexed, but not great.
Adding a hierarchyid field to the table would make the query simpler and far faster. Assuming there's a hierarchy field, the query would be just :
SELECT *
FROM ThatTable
ORDER BY hierarchy
Adding an index on hierarchy will this query and any query that looks eg for children of a specific node, very fast. Instead of querying recursively, the server only needs to look into that single index.
The article Lesson 1: Converting a Table to a Hierarchical Structure shows how to create a new table with a hierarchyid and populate it from parent/child data.

Related

MSSQL recursive query to find furthest parent

We have a db structure where each company has a parent company defined. What we want to do is walk up the structure from a given start point until the next 'most-parent' company is found and pull what that users assignment is to that 'most-parent' company. Below is a mock example.
+===================+ +=======+ +=============+
| Company | | User | | UserAccess |
+===================+ +=======+ +=============+
| id | | id | | id |
| Name | | Name | | fkUserId |
| fkParentCompanyId | +=======+ | fkCompanyId |
+===================+ | AccessLevel |
+=============+
+=======+
|Company|
+=============================================+
| id | Name | fkParentCompanyId |
+=============================================+
| 1 | ABC Corp | 1 |
| 2 | Outside Company | 1 |
| 3 | Inside Company | 1 |
| 4 | My Company | 3 |
| 5 | Other LLC | 4 |
| 6 | Yet Another Comp | 5 |
+=============================================+
+====+
|User|
+======================+
| id | Name |
+======================+
| 1 | Mike |
| 2 | Jackie |
| 3 | Sam |
+======================+
+==========+
|UserAccess|
+=================================================+
| id | fkUserId | fkCompanyId | AccessLevel |
+=================================================+
| 1 | 1 | 1 | Administrator |
| 2 | 2 | 1 | User |
| 3 | 3 | 1 | Administrator |
| 4 | 3 | 3 | Parent |
| 5 | 3 | 4 | Parent |
| 6 | 3 | 5 | Parent |
| 7 | 3 | 6 | Parent |
+=================================================+
So take 'Same', user.id == 3. I want to do a query that finds the nearest "not-Parent" relationship when given a starting Company.id along with Sam's User.id. Next is what I'm looking to get from the given inputs.
Inputs: User.id = 3, Company.id = 6
Output: Administrator
Inputs: User.id = 3, Company.id = 5
Output: Administrator
Inputs: User.id = 3, Company.id = 4
Output: Administrator
Inputs: User.id = 2, Company.id = 1
Output: User
I've been looking at recursive queries using the CTE model, having some difficulty understanding what's it's really doing and thus not yet able to translate that model into something that would work for the above example.
Any guidance would be greatly appreciated.
Update
Been working on CTE in SSMS and feel like I'm getting close... Here is an example query that I'm trying, but not getting the result I expect...
Here I'm trying to do a recursive CTE on Sam # "Yet Another Comp". What I want is a list of Sams access up the chain.
with cte(UserId, CompanyId, UserAccess, ParentCompanyId)
as
(
select
[UserId],
[CompanyId],
[UserAccess],
[ParentCompanyId]
from [UserAccess]
where [UserId] = 3 and [CompanyId] = 6
union all
select
[UserAccess].[UserId],
[UserAccess].[CompanyId],
[UserAccess].[AccessLevel],
[cte].[ParentCompanyId]
from [UserAccess]
join [cte] on [UserAccess].[ParentCompanyId] = [cte].[CompanyId]
where [UserAccess].[UserId] = 3 [UserAccess].[CompanyId] != 6
)
select * from cte
I'm expecting this:
+===+
|cte|
+==========================================================+
| UserId | CompanyId | UserAccess | ParentCompanyId |
+==========================================================+
| 3 | 6 | Parent | 5 |
| 3 | 5 | Parent | 4 |
| 3 | 4 | Parent | 3 |
| 3 | 3 | Parent | 1 |
| 3 | 1 | Administrator | 1 |
+==========================================================+
But what I'm actually getting is this:
+===+
|cte|
+==========================================================+
| UserId | CompanyId | UserAccess | ParentCompanyId |
+==========================================================+
| 3 | 6 | Parent | 5 |
+==========================================================+
The recursive query isn't pulling additional rows into the table. So I commented out the where statement on the recursive query, expecting to get a max recursion error and see a blip of all of the results. But no, still just get the single row back.

Create materialized view from multiple tables with identical schemas

I am trying create a materialized view in Redshift.
I have 100 tables of the form
user_1
user_2
...
user_100
Each table has the same schema. For example:
user_1
| id | user_id | date | expense |
|----|---------|----------|---------|
| 1 | 1 | 20200521 | 200 |
| 2 | 2 | 20200601 | 100 |
| 3 | 1 | 20200603 | 90 |
user_2
| id | user_id | date | expense |
|----|---------|----------|---------|
| 1 | 1 | 20200521 | 250 |
| 2 | 3 | 20200204 | 10 |
| 3 | 2 | 20200403 | 50 |
What I want to do is to create a materialized view which has all the rows from all 100 tables.
The objective is to run a queries to calculate the sum(expense) for given user_id between certain dates.
So my materialized view will be something like this:
user_1
| id | user_id | date | expense |
|----|---------|----------|---------|
| 1 | 1 | 20200521 | 200 |
| 2 | 2 | 20200601 | 100 |
| 3 | 1 | 20200603 | 90 |
| 4 | 1 | 20200521 | 250 |
| 5 | 3 | 20200204 | 10 |
| 6 | 2 | 20200403 | 50 |
I am having trouble with the CREATE query for this view.
Any guidance on the query to create this view is appreciated.
Thank you.
How about UNIONing the tables?
Keep in mind the difference between UNION (does implicit distinct) and UNION ALL which just combines rows from two tables as they come.
CREATE MATERIALIZED VIEW users
AS
SELECT * FROM user_1 UNION ALL
SELECT * FROM user_2 UNION ALL
SELECT * FROM user_3 UNION ALL
...
SELECT * FROM user_100

Find the newest entry of a crosstable per record?

I have three tables:
My products with their IDs and their features.
is a table with treatments of my products with a treatment-ID, a method, and a date. The treatments are done in batches of many products so there is a crosstable
with the products IDs and the treatment IDs and a bool value for the success of the treatment.
Each product can undergo many different treatments so there is a many-to-many relation. I now want to add to the product table (1.) for every product a value that shows the method of its most recent successful treatment if there is any.
I made a query that groups the crosstable's entries by product-ID but I don't know how to show the method and date of it's last treatment.
table 1:
| productID | size | weight | height | ... |
|-----------|:----:|-------:|--------|-----|
| 1 | 13 | 16 | 9 | ... |
| 2 | 12 | 17 | 12 | ... |
| 3 | 11 | 15 | 15 | ... |
| ... | ... | ... | ... | ... |
table 2:
| treatmentID | method | date |
|-------------|:--------:|-----------:|
| 1 | dye blue | 01.02.2016 |
| 2 | dye red | 01.02.2017 |
| 3 | dye blue | 01.02.2018 |
| ... | ... | ... |
table 3:
| productID | treatmentID | success |
|-----------|:-----------:|--------:|
| 1 | 1 | yes |
| 1 | 2 | yes |
| 1 | 3 | no |
| ... | ... | ... |
I need table 1 to be like:
table 1:
| productID | size | weight | height | latest succesful method |
|-----------|:----:|-------:|--------|-------------------------|
| 1 | 13 | 16 | 9 | dye red |
| 2 | 12 | 17 | 12 | ... |
| 3 | 11 | 15 | 15 | ... |
| ... | ... | ... | ... | ... |
My query:
SELECT table3.productID, table2.method
FROM table2 INNER JOIN table3 ON table2.treatmentID = table3.treatmentID
GROUP BY table3.productID, table2.method
HAVING (((table3.productID)=Max([table2].[date])))
ORDER BY table3.productID DESC;
but this does NOT show only one (the most recent) entry but all of them.
Simplest solution here would be to write either a subquery within your sql, or create a new query to act as a subquery(it will look like a table) to help indicate(or elminate) the records you want to see.
Using similar but potentially slightly different source data as you only gave one example.
Table1
| ProductID | Size | Weight | Height |
|-----------|------|--------|--------|
| 1 | 13 | 16 | 9 |
| 2 | 12 | 17 | 12 |
| 3 | 11 | 15 | 15 |
Table2
| TreatmentID | Method | Date |
|-------------|------------|----------|
| 1 | dye blue | 1/2/2016 |
| 2 | dye red | 1/2/2017 |
| 3 | dye blue | 1/2/2018 |
| 4 | dye yellow | 1/4/2017 |
| 5 | dye brown | 1/5/2018 |
Table3
| ProductID | TreatmentID | Success |
|-----------|-------------|---------|
| 1 | 1 | yes |
| 1 | 2 | yes |
| 1 | 3 | no |
| 2 | 4 | no |
| 2 | 5 | yes |
First order of business is to get the max(dates) and productIds of successful treatments.
We'll do this by aggregating the date along with the productIDs and "success".
SELECT Table3.productid, Max(Table2.Date) AS MaxOfdate, Table3.success
FROM Table2 INNER JOIN Table3 ON Table2.treatmentid = Table3.treatmentid
GROUP BY Table3.productid, Table3.success;
This should give us something along the lines of:
| ProductID | MaxofDate | Success |
|-----------|-----------|---------|
| 1 | 1/2/2018 | No |
| 1 | 1/2/2017 | Yes |
| 2 | 1/4/2017 | No |
| 2 | 1/8/2017 | Yes |
We'll save this query as a "regular" query. I named mine "max", you should probably use something more descriptive. You'll see "max" in this next query.
Next we'll join tables1-3 together but in addition we will also use this "max" subquery to link tables 1 and 2 by the productID and MaxOfDate to TreatmentDate where success = "yes" to find the details of the most recent SUCCESSFUL treatment.
SELECT table1.productid, table1.size, table1.weight, table1.height, Table2.method
FROM ((table1 INNER JOIN [max] ON table1.productid = max.productid)
INNER JOIN Table2 ON max.MaxOfdate = Table2.date) INNER JOIN Table3 ON
(Table2.treatmentid = Table3.treatmentid) AND (table1.productid = Table3.productid)
WHERE (((max.success)="yes"));
The design will look something like this:
Design
(ps. you can add queries to your design query editor by clicking on the "Queries" tab when you are adding tables to your query design. They act just like tables, just be careful as very detailed queries tend to bog down Access)
Running this query should give us our final results.
| ProductID | Size | Weight | Height | Method |
|-----------|------|--------|--------|-----------|
| 1 | 13 | 16 | 9 | dye red |
| 2 | 12 | 17 | 12 | dye brown |

Count rows which has the same ID and display on the table

This is the original table:
| ID | Card_No |
|----+---------|
| 1 | 6453671 |
| 1 | 8795732 |
| 1 | 9948495 |
| 2 | 7483009 |
| 2 | 1029001 |
| 3 | 7463094 |
Is it possible to make it like this? Which will be adding a calculated column the the original table?
| ID | Card_No | Total |
|----+---------|-------|
| 1 | 6453671 | 3 |
| 1 | 8795732 | 3 |
| 1 | 9948495 | 3 |
| 2 | 7483009 | 2 |
| 2 | 1029001 | 2 |
| 3 | 7463094 | 3 |
I'm using Microsoft Access, and I've tried code like this:
SELECT ID, COUNT (*) AS Total FROM Table GROUP BY ID
But I did not get the result I want.
First of all, saving the calculated value back into table is not only unnecessary but bad design.
Options:
build a report that counts records with an expression in textbox
build aggregate query then another query joining aggregate query to the table
DCount() domain aggregate function in query

Add one with same foreign key at row sql server

I have a problem on sql server.
How to get running number from foreign key in one time select data from table?
example :
I have one table such as
-----------------
| id | pid | desc |
-----------------
| 1 | 1 | a |
| 2 | 1 | b |
| 3 | 1 | c |
| 4 | 2 | d |
| 5 | 2 | e |
| 6 | 2 | f |
| 7 | 2 | g |
| 8 | 3 | h |
| 9 | 3 | i |
| 10 | 1 | j |
| 11 | 1 | k |
-----------------
I want to get result as below
------------------------
| id | pid | desc | rec |
------------------------
| 1 | 1 | a | 1 |
| 2 | 1 | b | 2 |
| 3 | 1 | c | 3 |
| 4 | 2 | d | 1 |
| 5 | 2 | e | 2 |
| 6 | 2 | f | 3 |
| 7 | 2 | g | 4 |
| 8 | 3 | h | 1 |
| 9 | 3 | i | 2 |
| 10 | 1 | j | 4 |
| 11 | 1 | K | 5 |
------------------------
In above tables foreign key ('pid') Column has values 1 to 3 in different row numbers.
I tried to get the running number from each 'pid' field name.
I havn't found any way to do this,
Can I do that? Can some one help me? am still newbie at sql server
Try this
SELECT
id,
pid,
[desc],
Row_Number() OVER (PARTITION BY pid ORDER BY id) AS rec
FROM <yourtable>
ORDER BY id
You can use Ranking function in SQL Server 2005+ to accomplish that,
So here is your query
Select Row_Number() over (partition by pid order by id) as rec , * from Table

Resources