How to retrieve desired records? - sql

Input Table : Regions
+---------------+---------------+---------- +-----------+
| Child | Parent | Level | levelname|
+---------------+---------------+---------- +-----------+
| All Region | All Region | 1 | national |
| Africa Region | All Region | 2 | region |
| America | All Region | 2 | region |
| Asia | All Region | 2 | region |
| Europe Region | All Region | 2 | region |
| Africa | Africa Region | 3 | Subregion |
| Asia Pacific | Asia | 3 | Subregion |
| Europe | Europe Region | 3 | Subregion |
| North America | America | 3 | Subregion |
| South America | America | 3 | Subregion |
| Argentina | South America | 4 | Country |
| Australia | Asia Pacific | 4 | Country |
| Pakistan | Asia Pacific | 4 | Country |
| South Africa | Africa | 4 | Country |
| Tunisia | Africa | 4 | Country |
| Uruguay | South America | 4 | Country |
+-------------------------------------------------------+
Here , regions are of 4 levels
All region
Region
Sub Region
Country
they have 0,1,2 and 3 ancestors,such as a country has subregion,region and allregion as ancestors ,suppose we give "Uruguay" ,then output will be South America, America , All Region.
Now, I need a query for this table,which will retrieve all ancestors for a given "child"

Your best bet is a recursive CTE:
With recRegions AS
(
/*Recursive Seed*/
SELECT
Child,
Parent,
Level,
0 as Depth,
CAST(Child as VARCHAR(5000)) as Path
FROM
Regions
WHERE
Child=<WhateverChildYouAreWanting>
UNION ALL
/*Recursive Term*/
SELECT
Regions.Child,
Regions.Parent,
Region.Level,
recRegions.Depth + 1,
recRegions.Path || '>' || Region.Child
FROM
recRegions
INNER JOIN Regions on
recRegions.parent = Regions.Child
Where recRegions.Depth < 10
)
Select Parent as Ancestors FROM recRegions;
Recursive queries can be a little tricky to wrap your head around at first, but if you break up the pieces of it, it makes sense:
Recursive Seed - This is the part where we get the first term we are after. In your case, we just want the record where the Child is the country you are wanting to query.
Recursive Term - This is the part where the query refers back to itself. It joins the recursive CTE recRegions to your Region table, connecting the child to the parent. The DB will hit this recursive term until no more records come back, which means we've climbed all the way up your hierarchy.
The final select statement just pulls back the records from your recursive query. You wanted all the ancestors, so that would be all of the Parent field records.
Generally when you see a table with a layout child | parent | attributes | of | that | relationship you can turn to the super powerful recursive CTE to make quick sense out of it all.
As #dnoeth mentioned in your Q's comments, you could also join Regions table to itself 4 times since your hierarchy seems to be only 4 deep. A recursive query doesn't care about depth though, so if you add more depth to your hierarchy, you won't have to edit your SQL to pull the ancestors.
Updated to add a "Depth" field to track recursions and stop after 10. Also added a "Path" field to track the hierarchy as it's built up from the Child. If you have an issue with your hierarchy cycling (a child reporting to a parent that reports to the child causing an endless loop) then you can use the following SQL statement instead of the SELECT parent FROM version above:
SELECT * FROM recRegions;
Now you will see Path and Depth of each node of your hierarchy so you can fix your data, or the recursive CTE to avoid the cycling.

Related

Is it possible to get measure value from facts table for a parent member of a hierarchy ignoring the children's values?

We are creating a Financial OLAP cube with a parent-child hierarchy for the financial entities. The facts table contains measures for both the parent and child entities and the parent's value is not equal to the total of its children's values. For example, in facts table we have:
+----------+----------+
| EntityID | NetSales |
+----------+----------+
| Europe | 15000 |
| Spain | 3500 |
| France | 8000 |
| Italy | 5000 |
+----------+----------+
Spain, France and Italy are children of Europe but Europe's total Net Sales is not the total of Spain + France + Italy due to eliminations done when sales are done between those children (Spain to France, etc.).
We need to create a dimension in the cube that will show the correct total of the parent Entity based on it's value on the facts table and not the aggregation of its children.
Is it possible to configure SSAS model or add custom MDX code to "overwrite" the value of the parent entity with the facts value for that entity and disregard the total of its children?
Note: there are 6 levels in the hierarchy, each parent level should take its own facts value for the measure and not the total of its children.
Expected result in cube would be:
+----------+----------+
| Entity | NetSales |
+----------+----------+
| Europe | 15000 |
| Spain | 3500 |
| France | 8000 |
| Italy | 5000 |
+----------+----------+
Actual result in cube with normal aggregation:
+----------+----------+
| Entity | NetSales |
+----------+----------+
| Europe | 16500 |
| Spain | 3500 |
| France | 8000 |
| Italy | 5000 |
+----------+----------+
Any advice will be highly appreciated.

Recursive SQL that gets the first instance of a value up a hierarchy

I have to do this in SQL.
I have a table called 'locations'. It contains a list of locations ranging from houses, to streets, to cities all the way up to continents.
locationId | name | desiredValue
1 | Wimbledon |
2 | Peckham |
3 | London |
4 | UK |
5 | France | 123
6 | Europe | 456
7 | Australia |
8 | Paris |
I have a second table called 'links' which contains the link of locations, and their relation
Location1 | Location2 | Linktype
3 | 1 | 5
3 | 2 | 5
4 | 3 | 5
6 | 4 | 5
5 | 8 | 5
linktype 5 indicates that location2 is situated 'in' location1. In the example above, locationId 1 (wimbledon) is located 'in' locationId 3 (london). LocationId 3 (london) is located 'in' locationId 4 (Europe) and so on.
The linktype just describes this 'in' relationship - the link table contains other relations as well which are not pertinent to this question, I just mention it in case it needs to be in a where clause.
For a given location, I want to get the first instance in its location hierarchy that has a 'desiredValue'
For example:
if I was interested in Peckham, I'd like to see that Peckham has no value, that London has no value, that UK has no value but that Europe does (456).
If I was interested in London, I'd see that it has no value, nor does the UK, but that Europe does (456)
If I was interested in Europe, I'd see that it has a value (456)
If I was interested in Paris, I'd see that it has no value, but France does (123)
I know I should probably be using recursive CTEs for this, but I'm stumped. Any help would be greatfuly received!

Updating a column in PL/SQL

(Using PL/SQL anonymous program block)
I have a table tblROUTE2 of Mexican state highways:
+-----------------+------------+---------+----------+----------+----------------------------+-----------+--------+
| TYPE | ADMN_CLASS | TOLL_RD | RTE_NUM1 | RTE_NUM2 | STATEROUTE | LENGTH_KM | STATE |
+-----------------+------------+---------+----------+----------+----------------------------+-----------+--------+
| Paved Undivided | Federal | N | 81 | | Tamaulipas Federal Hwy 81 | 124.551 | NULL |
| Paved Undivided | Federal | N | 130 | | Hidalgo Federal Hwy 130 | 76.347 | NULL |
| Paved Undivided | Federal | N | 130 | | Mexico Federal Hwy 130 | 68.028 | NULL |
+-----------------+------------+---------+----------+----------+----------------------------+-----------+--------+
and tblSTATE2 of Mexican states:
+------+-----------------------+---------+-----------+
| CODE | NAME | POP1990 | AREA_SQMI |
+------+-----------------------+---------+-----------+
| MX02 | Baja California Norte | 1660855 | 28002.325 |
| MX03 | Baja California Sur | 317764 | 27898.191 |
| MX18 | Nayarit | 824643 | 10547.762 |
+------+-----------------------+---------+-----------+
I need to update the STATE field in tblROUTE2 with the CODE field found in tblSTATE2, based on the route name in tblROUTE2. Basically, I need to somehow take the first string or two (some routes have two names)-- before the string 'Federal'-- of the STATEROUTE field in tblROUTE2 and make sure it matches with the string in the NAME field in tblSTATE2. Then since the states are matched with a CODE, update those codes in the STATE field of tblROUTE2.
I have started a code:
DECLARE
state_code
tblROUTE2.STATE%TYPE;
state_name
tblSTATE2.NAME%TYPE;
BEGIN
SELECT STATE, NAME
INTO state_code
FROM tblROUTE2 r, tblSTATE2 s
WHERE STATEROUTE LIKE '%Federal';
END;
As well, I will need to remove the state name from the route name. For example, the string in STATEROUTE 'Tamaulipas Federal Hwy' becomes 'Federal Hwy'. I have started a code, not sure if it's right:
UPDATE tblROUTE2
SET STATEROUTE = TRIM(LEADING FROM 'Federal');
Using MERGE update :
MERGE INTO tblROUTE2 A
USING
(
SELECT CODE, NAME FROM tblSTATE2
) B
ON
(
upper(SUBSTR(A.STATEROUTE, 0, INSTR(UPPER(A.STATEROUTE), UPPER('FEDERAL'))-2)) = upper(B.NAME)
)
WHEN MATCHED THEN UPDATE
SET A.STATE = B.CODE;
Here in FIDDLE I've replicated your tables and added additional record where STATEROUTE matches one of the records in NAME. Although Fiddle return an error, I ran it in my Oracle DB, and one record was updated correctly as the following screenshot:

Counting fields in a group by, and generating a greport with ms access

So I have this table
City | Status | District | Revenue
------------------------------------------
Oakland | Executed | North | $9.50
Los Angeles| Cancelled| South | $0.05
Oakland | Executed | North | $0.99
Oakland | Cancelled| North | $98.40
Sacramento | Executed | North | $43.50
Sacramento | Cancelled| North | $5.40
Los Angeles| Cancelled| South | $5.30
So I need this report that reads like this:
North District | Executed | Cancelled | Revenue
--------------------------------------------------------
Oakland | 2 | 1 | Sum of revenue
Sacramento | 1 | 1 | Sum of revenue
--------------------------------------------------------
South District | Executed | Cancelled | Revenue
--------------------------------------------------------
Los Angeles | 0 | 2 | Sum of revenue
But I'm stuck on how to create a query that groups and counts instances of specific values inside that group.
I mean I know syntax of group statements and count statements, but the counting a specific number of instances of a row inside a group seems pretty different than a regular count.
Can anyone guide me in the right direction? I'm not asking anyone to do my work (this isn't even a full sample of what I have to do) but if someone can help me with a statement that groups and counts specific rows in the group, with a SQL statement or an Access function, that would be awesome. From there I'd be able to figure out everything else.
Hey I ran across an answer actually. I just had to use Sum(IIF()) and it worked correctly.
SELECT
Test.City,
=Sum(IIf(Status="Cancelled",1,0))
FROM Test
Group BY Test.City

MySQL world database Trying to avoid subquery

I'm using the MySQL WORLD database.
For each Continent, I want to return the Name of the country with the largest population.
I was able to come up with a query that works. Trying to find another query that uses join only and avoid the subquery.
Is there a way to write this query using JOIN?
SELECT Continent, Name
FROM Country c1
WHERE Population >= ALL (SELECT Population FROM Country c2 WHERE c1.continent = c2.continent);
+---------------+----------------------------------------------+
| Continent | Nanme |
+---------------+----------------------------------------------+
| Oceania | Australia |
| South America | Brazil |
| Asia | China |
| Africa | Nigeria |
| Europe | Russian Federation |
| North America | United States |
| Antarctica | Antarctica |
| Antarctica | Bouvet Island |
| Antarctica | South Georgia and the South Sandwich Islands |
| Antarctica | Heard Island and McDonald Islands |
| Antarctica | French Southern territories |
+---------------+----------------------------------------------+
11 rows in set (0.14 sec)
This is the "greatest-n-per-group" problem that comes up frequently on StackOverflow.
SELECT c1.Continent, c1.Name
FROM Country c1
LEFT OUTER JOIN Country c2
ON (c1.continent = c2.continent AND c1.Population < c2.Population)
WHERE c2.continent IS NULL;
Explanation: do a join looking for a country c2 that has the same continent and a greater population. If you can't find one (which is indicated by the outer join returning NULL for all columns of c2) then c1 must be the country with the highest population on that continent.
Note that this can find more than one country per continent, if there's a tie for the #1 position. In other words, there could be two countries for which no third country exists with a greater population.