In Oracle 12c, I have a view, which takes a little time to run. When I add the where clause, it will return exactly one row of interest. The row has columns/value like this...
I need this flipped so that I can see one row per EACH "set". I need the SQL to return something like
I know I can do a UNION ALL for each of the entry sets, but as the view takes a little while to run, plus there are about 30 different sets (I only showed 3 - Car, Boat, and truck)
Is there a better way of doing this? I have looked at PIVOT/UNPIVOT, but I didn't see how to make this work.
I think you are looking for UNPIVOT
WITH TEMP_DATA (ID1, CarPrice, CarTax, BoatPrice, BoatTax, TruckPrice, TruckTax)
AS (
select 'AAA', 1, 2, 3, 4, 5, 6 from dual )
select TYPE, PRICE, TAX
from temp_data
unpivot
(
(PRICE, TAX)
for TYPE IN
(
(CarPrice, CarTax) as 'CAR',
(BoatPrice, BoatTax) as 'BOAT',
(TruckPrice, TruckTax) as 'TRUCK'
)
)
;
OUTPUT:
TYPE PRICE TAX
----- ---------- ----------
CAR 1 2
BOAT 3 4
TRUCK 5 6
I have a table called sales. In it there are columns catid, desc, parentforeignkey and the records are something like this:
catid desc parentforeignkey
1, clothes, 1
2, shoes, 1
3, socks, 1
4, gloves, 1
5, mittens, 4
6, leather gloves, 4
7, plain gloves, 4
...
How do I build a query to show this relationship?
I'ma take a shot at this, but I'm struggling to see the question. I feel like you want a query that groups the selection by the parent foreignkey and then lists the catid and desc for each parent. So basically something like
SELECT t.parentforeignkey, t.catid, t.desc
FROM table1 as t
GROUP BY t.parentforeignkey, t.catid, t.desc;
NOTE: be careful with "desc" as a column name as DESC is a reserved word for descending (used for sorting)
That will give you a result like:
ParentForeignKey | CatID | Desc
1 1 Clothes
1 2 Shoes
2 2 Shoes
3 1 Clothes
So the trick is to use GROUP BY to assign the parent and child groups. Be careful though, because the order of the group by command matters (GROUP BY Catid, ParentforeignKey yields a different result than what I listed above). Also, you need to explicitely say how each column is related to the grouping. If you leave a column out, you'll likely get an error (depending on your DBMS) that says something like "You tried to specify a query that does not include the specified expression as part of the aggregate function"
EDIT: I now see that you've included the DBMS in your question. If you're using the BIDS or SSRS then this is supremely easy, you'll what a query that just selects the data (and filters whatever you want out) and then you'll go to the tablix controls and define the parent group to the details as catid, and the parent of catid as foreignparentkey and then the table should take care of itself!
I have an Access db table, that holds couples of values: (SourceId, DestinationId). Both values are taken from the same list of Id's.
I want to create a list (query result?) of all item's ancestors. Ie, if the user enters Id=15, I'd like to return all Id's that are destinations for source-15, but also their destinations etc.
For example, if my table hold
15, 3 |
15, 4 |
4, 7 |
4, 8 |
3, 5 |
5, 2 |
1, 9
Id like to return 3, 4, 7, 8, 5, 2 (but not return 9).
I guess the solution should include some VBA code with loops or recursion, but I got confused by recordsets versus collections.
Any idea?
Thanks,
Aviram
Unfortunately Access SQL lacks the CONNECT BY syntax that Oracle uses to do hierarchical queries. However, if you are prepared to create a temporary table you can emulate it in Access with a loop.
In this example your original table is "LinkTab" and the temporary table will be "TmpTree" an you are starting from SourceID 15.
First execute:
SELECT SourceID, DestID, 1 as Lvl INTO TmpTree FROM LinkTab WHERE SourceID = 15
Then in a loop, repeatedly execute:
INSERT INTO TmpTree ( SourceID, DestID, Lvl )
SELECT newrows.SourceID, newrows.DestID, TmpTree.Lvl + 1
FROM TmpTree INNER JOIN LinkTab newrows ON TmpTree.DestID = newrows.SourceID
WHERE TmpTree.Lvl = (SELECT MAX(Lvl) FROM TmpTree)
until the statement returns zero affected rows (or if you can't detect this, count the rows in TmpTree each time and stop when they don't increase)
Now your results can be retrieved with :
SELECT DestID FROM TmpTree
And finally, to tidy up:
DROP TABLE TmpTree
I've tested these statements in the Access Query designer and they get the desired result.
I have some data I am querying. The table is composed of two columns - a unique ID, and a value. I would like to count the number of times each unique value appears (which can easily be done with a COUNT and GROUP BY), but I then want to be able to count that. So, I would like to see how many items appear twice, three times, etc.
So for the following data (ID, val)...
1, 2
2, 2
3, 1
4, 2
5, 1
6, 7
7, 1
The intermediate step would be (val, count)...
1, 3
2, 3
7, 1
And I would like to have (count_from_above, new_count)...
3, 2 -- since three appears twice in the previous table
1, 1 -- since one appears once in the previous table
Is there any query which can do that? If it helps, I'm working with Postgres. Thanks!
Try something like this:
select
times,
count(1)
from ( select
id,
count(distinct value) as times
from table
group by id ) a
group by times
I have a table for a menu system with the following structure, and some data.
ID, Text, ParentID, DestinationID
1, Applications, (null), (null)
2, Games, (null), (null)
3, Office, 1, (null)
4, Text Editing, 1, (null)
5, Media, (null), (null)
6, Word, 3, 1
7, Excel, 3, 2
8, Crysis, 2, 3
What I need is a query to which I can pass the menu ID, and it will return a list of items that have that ID as a child. BUT, I need it to only return children that have a valid path to a destination. So in the example above, the user will be presented initially with (Applications, Games), when he selects Applicaions, he is presented with (Office). Text Editing and Media should be omitted, because there are no valid destinations beneath them.
The trickiest thing about this, is that there is no predetermined depth to any given menu.
EDIT:
Today, the problem came up for MS SQL 2008, but in the past 2 weeks I've needed similar solutions for SQLite and SQL CE. The ideal solution should not be tied to any specific SQL engine.
SQL server only, but it sounds like a job for Common Table Expressions.
In Oracle:
SELECT m.*, level
FROM my_table m
START WITH
Id = :startID
CONNECT BY
ParentID = PRIOR Id
AND DestinationID IS NOT NULL
There is no way to do it in ANSI SQL with a single query. You may create an additional column AccessPath for you table:
ID, Text, ParentID, DestinationID AccessPath
1, Applications, (null), (null), "1"
2, Games, (null), (null), "2"
3, Office, 1, (null), "1.3"
4, Text Editing, 1, (null), "1.4"
5, Media, (null), (null), "5"
6, Word, 3, 1, "1.3.6"
7, Excel, 3, 2, "1.3.7"
8, Crysis, 2, 3, "1.2.8"
, and query:
SELECT mp.Id, mp.Text
FROM my_table mp, my_table mc
WHERE mp.parentID = #startingParent
AND mc.Id <> mp.Id
AND SUBSTR(mc.AccessPath, LENGTH(mp.AccessPath)) = mp.AccessPath
GROUP BY
mp.Id, mp.Text
It's a bad idea to start with NULL, as the index on ParentID cannot be used in this case. For a start, use a fake parentID of 0 instead of NULL.
If the hierarchy/tree that you are stroing in your database does not change very often, I would recommend to use the modified preorder tree traversal (MPTT) algorithm. That would require a different table schema, but would allow you to request a whole subtree with a simple SQL statement (no recursion, etc.).
The article on Storing Hierarchical Data in a Database describes this method in detail.
In your example you would get the following tree, where I call the red numbers the left value and the green right value of a node.
Now, if you want to select the Office subtree, you can do this with:
SELECT * FROM tree WHERE left BETWEEN 10 AND 15 AND destination IS NOT NULL
If your DB does not support the BETWEEN statement, you can of course write left > 10 AND left < 15 instead.
Your table would look like this:
name | left | right | destination
------------------------------------------
root | 1 | 17 | NULL
Applications | 7 | 16 | ...
...
If this is a problem that interests you (or plagues you), you may want to check out: Joe Celko's Trees and Hierarchies in SQL for Smarties.
As others have pointed out, there's no way in standard ANSI SQL to do what you want. For something like this, I once implemented on SQL 2000 a system for tracking components of products an ex employer made - each "product" could be atomic component like, say, screw A500. This component could be used in "composite" components: some A500 screws plus 6 B120 wood boards conformed a C90 "stylish tool box". That box, plus more screws and a motor "M500" could conform a carpetry tool.
I designed a table "Product" like this:
ID, PartName, Description
1, A500, "Screw A500"
2, B120, "Wood panel B120"
3, C90, "Stylish tool box C90"
4, M500, "Wood cutter M500"
And a "ProductComponent" table as follows:
Hierarchy, ComponentID, Amount
0301, 1, 24
0302, 2, 6
0401, 1, 3
0402, 3, 1
0403, 4, 1
040201, 1, 24
040202, 2, 6
The trick is: field hierarchy is a VARCHAR with first 2 chars representing each product's ID, each next pair of chars identify a node in the tree. So we see that product 3 depends on 2 other products. Product 4 depends on 2 others, also, one of which depends on its part on two others.
There's lots of redundancy in this model, but allows to easily calculate how many screws you need for a particular product, determine fastly which parts need wood panels or get the list of all components a product ultimately depends on (including indirect dependencies), etc. And scanning the tree below a certain level is a simple LIKE query!
By using 2 chars in a hexadecimal representation I limited a product to depend directly on maximum 256 other prods (which on turn can depend on something else). You could change that to use base 36 (the 26 letters plus 10 numbers) or base-64 if you need more than that.
Besides, this table model works very well on Access and mySQL, too. What you can not have is circular dependencies in any way.
SQL is not very good at walking arbitrary depth hierarchies.
If there's less than 1000 of these records, I would grab them all to the application and construct the graph there.
If there's more than 1000 of these records, I'd group them into raw subtrees of approx 1000 (by adding a SubtreeID foreign key) and fetch each subtree... then construct the graph of the subtree in the application.
The first thing I'd do is strip out the destination column - it's meaningless in terms of hierarchy (it actually appears to be a kind of second primary key to signal a live child row the way you've represented it)
this would give
ID, Item, parentID
1, Applications, (null)
2, Games, (null)
3, Office, 1
4, Text Editing, 1
5, Media, (null)
6, Word, 3
7, Excel, 3
8, Crysis, 2
e.g...
word > office > applications and...
excel > office > applications
...should presumably be on the same menu item (parent id 3)
I'm not sure how you're selecting the menu but I'll work on the principle that there's an initial menu button set with (null) as it's parameter and each subsequent click holds the next parameter in sequence dynamically (which seems to match your comments)
e.g.
click top level menu :- value is (null)
click applications :- value is 1
click Office :- value is 3
etc.
Assuming the destinationID is doing nothing apart from showing an active child link (allowing you to remove it), the code would then be as follows:
with items (nodeID, PID, list) as
(select id, ParentID, item
from menu
where id = 9
union all
select id, ParentID, item
from menu
inner join items on nodeID = menu.ParentID
)
select *
from items
where (pid = 9)
and nodeID in (select parentid from menu)
This works in MSSQL 2005+
If you need the destination id for some other reason then you can amend the code as follows (if you need to return the lowest level where a node id hasn't been set as a parent id, for instance):
with items (nodeID, PID, list, dest) as
(select id, ParentID, item, destinationID
from menu
where id = 9
union all
select id, ParentID, item, destinationID
from menu
inner join items on nodeID = menu.ParentID
)
select *
from items
where (pid = 9)
and (nodeID in (select parentid from menu)
or dest is not null)
https://geeks.ms/jirigoyen/2009/05/22/recursividad-con-sql-server/
ALTER PROCEDURE [dbo].[Usuarios_seguridad_seleccionar]
AS
BEGIN
DECLARE #minClave int
SELECT #minClave = MIN(Clave) FROM dbo.Usuarios_seguridad;
WITH UsuariosAccesos AS(
SELECT top 1 us1.Padre,us1.Clave,us1.Variable,us1.Modulo,us1.Contenido,us1.Acceso,us1.Imagen
FROM dbo.Usuarios_seguridad us1
WHERE us1.Clave = #minClave
UNION ALL
SELECT top 100 percent us2.Padre,us2.Clave,us2.Variable,us2.Modulo,us2.Contenido,us2.Acceso,us2.Imagen
FROM dbo.Usuarios_seguridad us2
INNER JOIN UsuariosAccesos AS us3 ON us3.Clave = us2.Padre
WHERE us2.Clave <> #minClave
)
SELECT TOP 100 PERCENT ia.Padre,ia.Clave,ia.Variable,ia.Modulo,ia.Contenido,ia.Acceso,ia.Imagen
FROM UsuariosAccesos ia
ORDER BY padre, clave
END
GO