ive

IoT Security Market Expected to Reach $56.2 Billion by 2029, Driven by 18.4% CAGR

(EMAILWIRE.COM, October 28, 2024 ) The global IoT security market size is projected to grow from USD 24.2 billion in 2024 to USD 56.2 billion by 2029 at a Compound Annual Growth Rate (CAGR) of 18.4% during the forecast period. Organizations face significant financial losses and reputational damage...




ive

Application Modernization Services Market Forecast 2024-2029: Growth, Demand, Key Drivers

(EMAILWIRE.COM, October 28, 2024 ) The Application Modernization Services Market is estimated at USD 19.82 billion in 2024 to USD 39.62 billion by 2029 at a Compound Annual Growth Rate (CAGR) of 14.8%, according to new research report by MarketsandMarkets™ Browse in-depth TOC on “Application...




ive

The Women’s Activewear Market was is expected to grow US$ Bn by 2030, as per Maximize Market Research.

(EMAILWIRE.COM, October 28, 2024 ) Women’s Activewear Market overview Women's activewear is clothing designed specifically for women to wear while engaging in physical activity and working out. It typically includes sports bras, leggings, shorts, tops, and jackets made from breathable, moisture-wicking...




ive

Cyanoacrylate Adhesives Market worth $2.56 billion by 2029

(EMAILWIRE.COM, October 29, 2024 ) The report "Cyanoacrylate Adhesives Market by Chemistry (Ethyl cyanoacrylate, Methyl cyanoacrylate), End-use Industry (Industrial, Woodworking, Transportation (Automotive, Aerospace, Medical, Electronics, Consumer) - Global Forecast to 2029 " The global Cyanoacrylate...




ive

The Automotive Adaptive Front Lighting Market is LED by Europe, as per Maximize Market Research.

(EMAILWIRE.COM, October 30, 2024 ) Growing vehicle manufacturing is driving the need for enhanced lighting systems worldwide. Increased visibility during nighttime decreases accidents, prompting more stringent regulations and greater consumer demand for these safety systems. Adaptive headlights are...




ive

Automotive Adaptive Front Lighting Market was is expected to grow $6.55 Bn by 2030, as per Maximize Market Research.

(EMAILWIRE.COM, October 31, 2024 ) Automotive Adaptive Front Lighting Market is expected to grow at a CAGR of 27.8% throughout the forecast period, to reach US$ 6.55 Bn. by 2030 The global automotive adaptive front lighting market is on the rise, driven by increasing safety concerns and consumer...




ive

UAV (Drone) Navigation System Market Forecast to 2030: Growth Driven by Military, Commercial, and Technological Advancements

(EMAILWIRE.COM, October 31, 2024 ) The UAV-Drone-Navigation System Market is expected to grow from USD 960 million in 2024 to USD 1,520 million by 2029, representing a CAGR of 11.2%. The increase in demand for UAVs across sectors, including commercial applications, military operations, and public...




ive

UAV (Drone) Propulsion Systems Market Set to Reach $11.1 Billion by 2029, Driven by Innovation in Power and Efficiency

(EMAILWIRE.COM, October 31, 2024 ) The UAV-Drone Propulsion Systems Market is forecasted to grow from USD 6,994 million in 2024 to USD 11,098 million by 2029, at a CAGR of 10.9%. As UAV applications expand across sectors like defense, government, and commercial use, demand is accelerating for more...




ive

Digital Instrument Cluster Market Growth Accelerates with Rising Demand in Automotive Sector, as per Maximize Market Research.

(EMAILWIRE.COM, November 01, 2024 ) The global Digital Instrument Cluster market is witnessing rapid growth, fueled by increasing demand for advanced automotive displays and enhanced in-car experiences. Driven by advancements in digital interfaces, these clusters provide customizable, high-resolution...




ive

Automotive Microcontrollers Market is dominated by Asia Pacific as per Maximize Market Research

(EMAILWIRE.COM, November 01, 2024 ) Automotive Microcontrollers Market is expected to reach US$ 22.93 Bn. by 2030, at a CAGR of 8.2% during the forecast period. The automotive microcontroller market is experiencing significant growth, driven by the increasing integration of electronics in vehicles....




ive

Antimicrobial Susceptibility Testing Market Expands as Need for Effective Infection Control Rises, as per Maximize Market Research

(EMAILWIRE.COM, November 03, 2024 ) The Antimicrobial Susceptibility Testing Market is experiencing growth due to rising concerns over antibiotic resistance and infection control. AST systems help healthcare providers determine the most effective treatments for infections, improving patient outcomes....




ive

Aluminum Flat Rolled Products Market Sees Strong Growth with Demand from Automotive and Construction Sectors, as per Maximize Market Research

(EMAILWIRE.COM, November 04, 2024 ) The Aluminum Flat Rolled Products Market is set for expansion as demand rises in automotive, construction, and packaging industries. Aluminum’s lightweight and corrosion-resistant properties make it ideal for use in energy-efficient vehicles and sustainable buildings....




ive

***** Locations Archive - Gama Aviation (rank 29)

25 Templer Avenue, Farnborough. 25 Templer Avenue, Farnborough is home to our Group headquarters and is the nerve centre for the European Air division. With excellent access to the facilities at the airport. Read more.




ive

COPD (Chronic Obstructive Pulmonary Disease)

Title: COPD (Chronic Obstructive Pulmonary Disease)
Category: Diseases and Conditions
Created: 12/31/1997 12:00:00 AM
Last Editorial Review: 5/6/2022 12:00:00 AM




ive

Oliver Brandt zur regionalen Steuerschätzung




ive

Saw a doctor (good adjustments); drew a pickle (while talking to friends); crossed a river.

from Instagram https://instagr.am/p/DCIneGJpq6Z/ via IFTTT





ive

New Hives: Rigor Mortis Radio

Video: The Hives – “Rigor Mortis Radio”

Directed by Filip Nilsson. From The Death of Randy Fitzsimmons, out now.

Remember when CGI was just CGI and not A.I.?

Or maybe the Hives are just really good dancers. It’s possible. Things are very groovy in Sweden and the people are very healthy and nimble. So you never know.

(Journalistic integrity spoiler: You, in fact, do know. The actual dancers — Alex Brown, Tom Hardiman, Connor Pearson, Reece Woodier, and Jacob Whawell — are credited in the video.)

I’ve seen the Hives in concert several times and their natural moves are impressive enough. Do they really need to employ deepfake technology to impress us even more? Maybe they do, because this video is fun and hilarious. And of course the song is great too.

But, like with all computer generated imagery, you find yourself looking for the glitches and cruising down the uncanny valley. It’s real looking enough, but not totally convincing, which makes it a little creepy. And, unsurprisingly, that creepiness factor works with the song’s lyrics.

You’ve never seen me look so good before
This silver lining and this golden glow
This shine, all mine
Looking like I’m fresh off an assembly line.

Read more at Glorious Noise...




ive

New Spring Silver: It’s Imperative

Video: Spring Silver – “It’s Imperative”

From Don’t You Think It’s Strange?, out now.

I don’t know anything about this artist but I somehow stumbled across this song and it’s too great to keep it to myself. Their little bandcamp bio says they’re from Silver Spring, Maryland and that the album was “written, performed, produced, and mixed by K Nkanza.” Well alright. What more do you need to know? They also offer thanks “to Mom for letting me record at her house.” It’s nice to see kids be grateful to their parents!

I see you all
Etched in the vertical tiles of an office building
I see you all
Trapped in the virtual squares of an application.

Sounds like somebody’s sick of zoom meetings!

Spring Silver: web, bandcamp, amazon, apple, spotify, wiki.

Read more at Glorious Noise...




ive

Absence of Attentiveness

From 1989 to 1994 there was a show with a prescient title and approach, a show that gave rise to people including Jon Stewart and Marc Maron: “Short Attention Span Theater.” It presented bits and clips, standup and skits. Quick takes in keeping with the short attention spans that were existing then and arguably shrinking even more so now.

Market research firm MIDiA has recently conducted its annual survey of independent labels and distributors to create what it describes as “the definitive view of the independent music economy.” Modesty notwithstanding, it has sliced, diced and riced data from 5,500 companies in that space—which represents $10.6-billion of recorded music revenue—so its assessment is undoubtedly substantive, especially as it adds in financials from major labels and artist distribution platforms, thereby achieving, it claims, 93% of all recorded music revenue on a global basis.

The big labels, of course, make big money. Universal Music Group reported its Q3 2024 earnings last week, which has it that for the first nine months of 2024 the company had revenues of €8,396 million, which is a 6.3% increase over the same period in 2023. Focusing just on the Recorded Music category, the revenues were €6,335 million, up 4.9% year-over-year—and last year wasn’t at all bad.

Read more at Glorious Noise...




ive

Parteiverrat




ive

Diebold Delivers Georgia for Republicans

In a follow up to his story on the 2004 election, "Was the 2004 Election Stolen," Robert Kennedy brings us, in the latest issue of Rolling Stone, specific details on how Diebold has rigged voting in Georgia, with the confessions of a Diebold employee, Chris Hood. "Will The Next Election Be Hacked," is a frightening article that shows exactly how far some corrupt politicians are willing to go to insure that they keep control of our government out of the hands of the people. Folks, our democracy is in danger.




ive

Sesame Street's 50th Anniversary Celebration!

Recommended

This second DVD release from Shout Factory Kids celebrating Sesame Street's 50th anniversary consists of a special made this year for prime time on both HBO (where the show has moved for its first run episodes) and PBS (which still presents the show delayed after the HBO showings, and with an annoying "E/I" symbol at the top of the screen to count towards the FCC's required hours of "educational programming" each week- in that regard it's likely for the better that its new primary home is HBO.) Like the 25th anniversary special, which was one of the very first DVD releases back in 1997, this focuses mostly on songs from the show but most of them are new performances, done on the show's set, rather than archival clips. There's a number of celebrity appearances but for some reason Joseph Gordon-Levitt, born after I had already outgrown the show on TV, was chosen to be the main "star". He takes a cab t...Read the entire review




ive

Magic Sinewave Executive Summary




ive

The Holy Spirit Pt1: Have You Received?

Our new series on 'The Holy Spirit' seeks to reintroduce us to the oft-neglected third Person of the Godhead. Even those who claim to move in the power of the Spirit don't always have a deep relationship with Him as a Person. So, join us as we seek a deeper knowledge and fellowship with 'God on Earth' - the Holy Spirit. In our first study, we ask the question: are you walking in everything of the Holy Spirit that God intended for you as His child? Acts 19 presents us with some 'disciples' who had not entered a full experience of the Holy Spirit. Let's see what we can learn from their interview with Paul, as he asks 'Have You Received?'. This message is available at https://www.preachtheword.com now in MP3 audio format and in HD video on our YouTube Channel (https://youtube.com/PreachTheWord)...



  • Religion & Spirituality

ive

The Holy Spirit Pt3: How To Receive

In Part 3 of our series of studies on 'The Holy Spirit', we investigate some biblical and practical steps to receiving the power of the Spirit, the power that you need to live the Christian life and serve Jesus effectively. We'll also consider some hindrances to the Spirit's power in our lives and churches as we learn 'How To Receive'. This message is available at https://www.preachtheword.com now in MP3 audio format and in HD video on our YouTube Channel (https://youtube.com/PreachTheWord)...



  • Religion & Spirituality

ive

Helping Others To Freedom Pt3: Forgiveness

In the third instalment of 'Helping Others To Freedom', we explore together one of the major blockages to blessing and freedom – unforgiveness. Resentment, bitterness and offense are obstacles that prevent many moving into the liberty and healing God intends for their lives. In this session we will see how unforgiveness imprisons us and can give the enemy rights to torture and torment us. We will also learn what true 'Forgiveness' is and some of the common misconceptions. Most importantly we'll give some helpful steps to move into the freedom of God's forgiveness for ourselves and toward others. This episode is available at https://www.preachtheword.com now in MP3 audio format and in HD video on our YouTube Channel (https://youtube.com/PreachTheWord)...



  • Religion & Spirituality

ive

Helping Others To Freedom Pt9: Practicalities And Mysteries Of Healing And Deliverance

In Session 9 of 'Helping Others To Freedom', David addresses many practical questions that often arise around healing and deliverance, also considering some of the mysterious matters that can confuse people. This episode on 'Practicalities And Mysteries Of Healing And Deliverance' should be extremely useful for anyone engaging in prayer ministry. This session is available at https://www.preachtheword.com now in MP3 audio format and in HD video on our YouTube Channel (https://youtube.com/PreachTheWord)...



  • Religion & Spirituality

ive

Helping Others To Freedom Pt10: Revival And Deliverance - The Bigger Picture

In the concluding session of 'Helping Others To Freedom', we look at how the principles of personal healing and deliverance apply to whole regions, and are essentially the same principles that can prepare a region for revival awakening. This is 'Revival And Deliverance - The Bigger Picture' - God sets individuals free in order to heal and deliver the world. This session is available at https://www.preachtheword.com now in MP3 audio format and in HD video on our YouTube Channel (https://youtube.com/PreachTheWord)...



  • Religion & Spirituality

ive

Adventures of Man in the Can - Ch 17 - The Great Exodus - The River



The Man in the Can and the animals from the park begin their trip down the river to the safe place found by the scout birds. What will be waiting for them on their journey?




MP3 File - Click Here to Download Podcast





GREAT DEALS FOR LISTENERS! CLICK BELOW
!


Save 10% on all your pet's needs at PetMeds.com
Up to 20% off on great athletic gear at Eastbay.com
Save 15% on your favorite stuff at Footlocker.com
Save 20% on your flower order with FTD Fluerop.com
Save $10 off your next order from PetCareRX.com




ive

Guest Post By Lynn AKA "Sign And Save Lives"

Guest post by Lynn

My name is Lynn, I’m 60 years old and I’d like to share my son’s illness with you all to try and help you understand the predicament thousands of others like my son are faced with next year, regarding DWP Benefit Reform for the sick and disabled.

My youngest son aged 40 has suffered from Paranoid Schizophrenia and emotional instability Personality Disorder since the age of 18. I have lived the illness with my son, the highs, the lows, and numerous hospital admissions over the years. My son had another very bad psychotic breakdown in August 2010, which resulted in a section 3 of the Mental Health Act and he has been in hospital care for 2 years this time.

Although my son is nearly 40 years old, mentally he is only 15, he is immature and extremely vulnerable and easily exploited. Therefore needs much support and care when living in the community.

My son, like thousands like him, who suffer from a Severe and Enduring mental health problem, has no voice when it comes to the DWP or ATOS, he like others wouldn’t be able cope with being interviewed next year 2013 when the Disability Living Allowance changes to Personal Independence Payment and he has to be assessed by DWP.  He, again like many others, who suffer severe mental illness, doesn’t believe he is ill, and just wants to be left alone.

The severely mentally ill will be hugely disadvantaged by the GOV/DWP/ATOS putting them through this ridiculous assessment in 2013, particularly people who don’t believe they are mentally ill, who to anyone who doesn’t know them, or, their background, can come across as fine, strong, healthy people, and may not appear ill at all, or, that they need help and support in their day to day lives, keeping them and others safe in the community, they won’t see the risks for the mentally ill, and many won’t have family or support to go with them to the assessments!

My son would tell an assessor he wasn’t ill, and say he just wanted to be left alone to get on with his life, which would see him losing his DLA/PIP for sure. Yet in reality, my son cannot get on a bus or use any public transport due to his paranoia that people are staring at him. He would get upset and get angry, probably end up getting in to trouble, because his self-esteem is low as is his confidence, and he thinks the world and his mate are against him.

To avoid this kind of stress and a way of coping, he needs taxis to anywhere he has to go, shopping, to any appointments he has. It is the only way he can cope with going out, and without his DLA/PIP he would be housebound and have no quality of life at what so ever.

If mental health sufferers lose their DLA/PIP benefit, (same as all disabled), it will place a huge extra burden on family, carers and friends emotionally and financially to provide the care and support and finance they need, which most of us couldn’t afford! All to often family/friends who are carers, as in our own case, are ill, worn down.

I was diagnosed 2 years ago with Moderate to Severe ME/CFS and told by my consultant it was due to being a carer for 22 years. My husband is 76 years old, in bad health, tired, it catches up with us and having to provide even more care to our loved ones should they lose their benefit, will put carers at more risk mentally and physically.

Because I understand the plight of the severely mentally ill, and have insight to the wider implications of what the GOV/DWP actions will cause for the sufferer and family, and carers, I have started this e-petition asking the Government to Exclude all severely mental ill from the 2013 DLA/PIP assessments, because what the GOV/DWP are doing is the biggest travesty and betrayal to our sick and disabled who in most cases have already proved their illness/disability with medical evidence and assessments they have already had to go through to get their DLA in the first place, and many like my son, were awarded DLA for an Indefinite period because they have provided proof their illness/disability is a life long one!

There are a number of e-petitions about Benefit Reform, I have signed them all which everyone should do to try and help protect all disabled, however, this petition is specifically to try and get the severely mentally ill EXCLUDED from the assessments because they do not have a strong enough voice and they will be hugely disadvantaged as I said before.

As a mum of a severely mentally ill son, and on behalf of all other sufferers like him, I know these assessments will cause SUICIDES in our most vulnerable, and cause huge hardship all round, please may I respectfully appeal to your good hearts and souls to support this e-petition and sign it, to share it with family & friends, on twitter/face book accounts, groups you may belong to, as I need 100,000 signatures to stand any chance of getting the House Of Commons to debate this, and at the moment I only have 2,456 signatures,  we only have until March to get the 100K!

Thank you all for taking the time to read this my plea, and I very much hope you will feel able to sign this petition.
http://epetitions.direct.gov.uk/petitions/35092

Kind Regards

Lynn
---------------------------------------

You can follow Lynn on Twitter  > @SignSaveLives




ive

Vijesti :: Svaki četvrti Hrvat pati od ‘nevidljive’ kronične bolesti, pitali smo liječnika kako prepoznati simptome

Autor: Redakcija 031 Naslov: Svaki četvrti Hrvat pati od ‘nevidljive’ kronične bolesti, pitali smo liječnika kako prepoznati simp
Postano: 13.11.2025. 20:15 (GMT 1) Ne postoji jedna tableta koja će smanjiti probavne tegobe i mogućnost razvoja probavnih bolesti, kaže nam odmah prof. dr. sc. Silvio Mihaljević, specijalist gastroenterologije iz Croatia poliklinike u Osijeku.

Kako navodi, stres, neredovita prehrana, hrana s puno masnoća i koncentriranih ugljikohidrata su sve redom faktori koji mogu dovesti do prolaznih probavnih tegoba, a, ako su pacijenti duže vremena izloženi nepravilnom stilu prehrane, mogu dovesti i do razvoja ozbiljnih bolesti, kao što su GERB, ulkusna bolest želuca i dvanaesnika, NASH, upalne bolesti crijeva, i slično.

Učestalost ovih problema pritom je sve veća. Primjerice, više od pola milijuna ljudi u Ujedinjenom Kraljevstvu ima upalnu bolest crijeva, od kojih su dva glavna oblika Crohnova bolest i ulcerozni kolitis. One nastaju kada imunološki sustav napadne crijeva, uzrokujući niz iscrpljujućih simptoma od bolova u trbuhu i gubitka težine do proljeva i krvi u stolici.

Vrlo često se gore navedene bolesti razvijaju tijekom dužeg vremenskog razdoblja s vrlo nespecifičnim tegobama u samom početku bolesti. Upravo je zato izuzetno važno obratiti pažnju na preventivne preglede, kaže prof. dr. sc. Mihaljević, jer postavljanje dijagnoze u ranoj fazi bolesti značajno doprinosi kraćem trajanju liječenja, manjom mogućnošću za razvojem brojnih komplikacija, a samim time i boljom kvalitetom života i puno kraćim izostankom s posla.

Uzmimo za primjer samo nealkoholnu bolest masne jetre, jednu od najčešćih uzroka kronične bolesti jetre te najbrže rastuće bolesti probavnog sustava. Prema posljednjim podacima, svaki četvrti građanin Hrvatske ima nealkoholnu masnu jetru kao posljedicu nezdrave prehrane, konzumacije alkohola i nekretanja. Prof. dr. sc. Mihaljević navodi stoga da je iznimno bitna redovita kontrola laboratorijskih nalaza koji ukazuju na funkciju jetre jer kronične bolesti nemaju značajnije probavne tegobe, nego samo blage i nespecifične smetnje kao brzo umaranje i opća slabost.

Te nespecifične simptome pacijenti ignoriraju tako da se vrlo često brojne kronične bolesti jetre pronađu u poodmakloj fazi kada je liječenje manje uspješno. Zbog svega navedenog redovite kontrole jetrenih nalaza su najbitnije u ranom otkrivanju kroničnih bolesti jetre”, kaže specijalist gastroenterologije.

Kako bi potaknula građane na preventivnu brigu o zdravlju, Croatia Poliklinika u Osijeku do kraja studenog omogućuje 20 posto popusta na endoskopske zahvate - gastroskopiju bez i s anestezijom i kolonoskopiju bez ili s anestezijom. Naime, osobe koje u anamnezi imaju rak nekog od organa probavnog sustava trebali bi u ranijoj životnoj dobi obavljati preventivne preglede svake dvije godine, a osobe starije životne dobi jednom godišnje. Brojni ljudi pritom odgađaju ranije spomenute endoskopske zahvate zbog neugodnosti ili straha, zbog čega prof. dr. sc. Mihaljević ističe da je važno pacijentima objasniti važnost obje pretrage jer se promjene koje se nađu tim pretragama vrlo često ne mogu dokazati nikakvim drugim laboratorijskim i radiološkim pretragama.

Kada im se objasni važnost navedenih pregleda te mogućnost dokumentiranja određenih promjena puno lakše će se odlučiti za navedene preglede. Prije gastroskopije možemo lokalno dati anestetik koji će u znatnoj mjeri umanjiti neugodnost samog pregleda, a prije kolonoskopije savjetujem uzimanje pola sata prije pregleda spazmolitika kako bi usporili peristaltiku crijeva i na taj način umanjili tegobe. Za one najzahtjevnije i najkompliciranije, oba pregleda možemo raditi u općoj anesteziji”, navodi prof. dr. sc. Mihaljević.


[Sponzorirani članak]




ive

La Chine et les pays d'Am�rique latine et des Cara�bes b�n�ficieront de perspectives plus prometteuses au cours des dix prochaines ann�es

Guid�e par la diplomatie des chefs d'Etat, la communaut� d'avenir partag� Chine-pays d'Am�rique latine et des Cara�bes b�n�ficiera de perspectives plus prometteuses durant la d�cennie � venir, a d�clar� mardi un porte-parole du minist�re chinois des...




ive

Chine : la partie continentale encourage les �changes entre les jeunes et appelle � lever les restrictions sur les voyages entre les deux rives du d�troit de Taiwan

La partie continentale de la Chine a r�affirm� son engagement � promouvoir les �changes entre les jeunes � travers le d�troit de Taiwan et a demand� aux autorit�s du Parti d�mocrate progressiste (PDP) de lever les restrictions sur les voyages �...




ive

Chine : la partie continentale d�clare que les consultations entre les deux rives du d�troit sont possibles en reconnaissant le Consensus de 1992

Les consultations entre l'Association pour les relations entre les deux rives du d�troit de Taiwan (ARATS) et la Fondation pour les �changes entre les deux rives du d�troit de Taiwan (SEF) pourraient reprendre si les autorit�s de Taiwan...




ive

Les �changes culturels entre les deux rives du d�troit se poursuivent malgr� les obstacles politiques

Une porte-parole de la partie continentale de la Chine a condamn� mercredi les politiciens du Parti d�mocrate progressiste (PDP) dans la r�gion de Taiwan, qui tentent de faire obstacle aux �changes normaux entre les deux rives du d�troit de Taiwan...




ive

Le commerce de la Chine avec les autres �conomies de l'APEC a atteint un niveau record entre janvier et octobre

Le commerce de la Chine avec les autres �conomies de l'APEC a atteint un niveau record de 21.270 milliards de yuans (environ 2.950 milliards de dollars) au cours des dix premiers mois de 2024, selon les donn�es publi�es mercredi par l'Administration...





ive

Inverted tables: an alternative to relational structures

articles: 

The inverted table format can deliver fast and flexible query capabilities, but is not widely used. ADABAS is probably the most successful implementation, but how often do you see that nowadays? Following is a description of how to implement inverted structures within a relational database. All code run on Oracle Database 12c, release 12.1.0.1.

Consider this table and a few rows, that describe the contents of my larder:

create table food(id number,capacity varchar2(10),container varchar2(10),item varchar2(10));
insert into food values(1,'large','bag','potatoes');
insert into food values(2,'small','box','carrots');
insert into food values(3,'medium','tin','peas');
insert into food values(4,'large','box','potatoes');
insert into food values(5,'small','tin','carrots');
insert into food values(6,'medium','bag','peas');
insert into food values(7,'large','tin','potatoes');
insert into food values(8,'small','bag','carrots');
insert into food values(9,'medium','box','peas');

The queries I run against the table might be "how many large boxes have I?" or "give me all the potatoes, I don't care about how they are packed". The idea is that I do not know in advance what columns I will be using in my predicate: it could be any combination. This is a common issue in a data warehouse.
So how do I index the table to satisfy any possible query? Two obvious possibilities:
First, build an index on each column, and the optimizer can perform an index_combine operation on whatever columns happen to be listed in the predicate. But that means indexing every column - and the table might have hundreds of columns. No way can I do that.
Second, build a concatenated index across all the columns: in effect, use an IOT. That will give me range scan access if any of the predicated columns are in the leading edge of the index key followed by filtering on the rest of the predicate. Or if the predicate does not include the leading column(s), I can get skip scan access and filter. But this is pretty useless, too: there will be wildly divergent performance depending on the predicate.
The answer is to invert the table:
create table inverted(colname varchar2(10),colvalue varchar2(10),id number);
insert into inverted select 'capacity',capacity,id from food;
insert into inverted select 'container',container,id from food;
insert into inverted select 'item',item,id from food;

Now just one index on each table can satisfy all queries:
create index food_i on food(id);
create index inverted_i on inverted(colname,colvalue);

To retrieve all the large boxes:
orclz> set autotrace on explain
orclz> select * from food where id in
  2  (select id from inverted where colname='capacity' and colvalue='large'
  3  intersect
  4  select id from inverted where colname='container' and colvalue='box');

        ID CAPACITY   CONTAINER  ITEM
---------- ---------- ---------- ----------
         4 large      box        potatoes


Execution Plan
----------------------------------------------------------
Plan hash value: 1945359172

---------------------------------------------------------------------------------
| Id  | Operation                                | Name       | Rows  | Bytes | C
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |            |     3 |   141 |
|   1 |  MERGE JOIN                              |            |     3 |   141 |
|   2 |   TABLE ACCESS BY INDEX ROWID            | FOOD       |     9 |   306 |
|   3 |    INDEX FULL SCAN                       | FOOD_I     |     9 |       |
|*  4 |   SORT JOIN                              |            |     3 |    39 |
|   5 |    VIEW                                  | VW_NSO_1   |     3 |    39 |
|   6 |     INTERSECTION                         |            |       |       |
|   7 |      SORT UNIQUE                         |            |     3 |    81 |
|   8 |       TABLE ACCESS BY INDEX ROWID BATCHED| INVERTED   |     3 |    81 |
|*  9 |        INDEX RANGE SCAN                  | INVERTED_I |     3 |       |
|  10 |      SORT UNIQUE                         |            |     3 |    81 |
|  11 |       TABLE ACCESS BY INDEX ROWID BATCHED| INVERTED   |     3 |    81 |
|* 12 |        INDEX RANGE SCAN                  | INVERTED_I |     3 |       |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("ID"="ID")
       filter("ID"="ID")
   9 - access("COLNAME"='capacity' AND "COLVALUE"='large')
  12 - access("COLNAME"='container' AND "COLVALUE"='box')

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

orclz>

Or all the potatoes:
orclz> select * from food where id in
  2  (select id from inverted where colname='item' and colvalue='potatoes');

        ID CAPACITY   CONTAINER  ITEM
---------- ---------- ---------- ----------
         1 large      bag        potatoes
         4 large      box        potatoes
         7 large      tin        potatoes


Execution Plan
----------------------------------------------------------
Plan hash value: 762525239

---------------------------------------------------------------------------------
| Id  | Operation                              | Name       | Rows  | Bytes | Cos
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |            |     3 |   183 |
|   1 |  NESTED LOOPS                          |            |       |       |
|   2 |   NESTED LOOPS                         |            |     3 |   183 |
|   3 |    SORT UNIQUE                         |            |     3 |    81 |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| INVERTED   |     3 |    81 |
|*  5 |      INDEX RANGE SCAN                  | INVERTED_I |     3 |       |
|*  6 |    INDEX RANGE SCAN                    | FOOD_I     |     1 |       |
|   7 |   TABLE ACCESS BY INDEX ROWID          | FOOD       |     1 |    34 |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("COLNAME"='item' AND "COLVALUE"='potatoes')
   6 - access("ID"="ID")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - this is an adaptive plan

orclz>

Of course, consideration needs to be given to handling more complex boolean expressions; maintaining the inversion is going to take resources; and a query generator has to construct the inversion code and re-write the queries. But In principle, this structure can deliver indexed access for unpredictable predicates of any number of any columns, with no separate filtering operation. Can you do that with a normalized star schema? I don't think so.
I hope this little thought experiment has stimulated the little grey cells, and made the point that relational structures are not always optimal for all problems.
--
John Watson
Oracle Certified Master DBA
http://skillbuilders.com




ive

Recursion with recursive WITH

articles: 

I recently had the opportunity to talk with Tom Kyte (!), and in the course of our conversation, he really made me face up to the fact that the SQL syntax I use every day is frozen in time: I’m not making much use of the analytic functions and other syntax that Oracle has introduced since 8i.

Here’s a brief history of these additions to Oracle SQL, from Keith Laker, Oracle’s Product Manager for Analytical SQL:

8i Window functions
9i Rollup, grouping sets, cube, enhanced window functions
10g SQL Model clause, statistical functions, partition outer join
11g SQL Pivot clause, Recursive WITH, Listagg, Nth value
12c Pattern matching, Top N

Not only do these make complex queries much, much simpler and easier, they are also much faster for the same result than non-analytic SQL, as Tom Kyte has shown repeatedly on his blog and in his books.

So, I was sold and I wanted to jump in with Recursive WITH. The WITH clause lets you define inline views to use across an entire query, and the coolest thing about this is that you can define the subquery recursively – so that the inline view calls itself.

Recursive WITH basic syntax:

WITH Tablename (col1, col2, ...) AS
(SELECT A, B, C... FROM dual                   --anchor member
UNION ALL
SELECT A', B', C'... from Tablename where...   --recursive member
)
select ... from Tablename where ...

Refactoring the Factorial

One fun thing about recursive WITH, aka recursive subquery refactoring, is the ease with which we can implement a recursive algorithm in SQL. Let’s warm up with a classic example of recursion: finding the factorial of a number. Factorial(n) = n! = 1*2*3*…*n . It’s a classic example because Factorial(n) can be defined recursively as:

Factorial(0) = 1
Factorial(n) = Factorial(n-1) * n

Here’s a first pass at implementing that directly in SQL to find the factorial of 5, using a recursive WITH subquery:

WITH Factorial (operand,total_so_far) AS
(SELECT 5 operand, 5 total_so_far FROM dual    -- Using anchor member to pass in "5"
UNION ALL
SELECT operand-1, total_so_far * (operand-1) FROM Factorial
WHERE operand > 1)
SELECT * FROM Factorial;

   OPERAND TOTAL_SO_F
---------- ----------
         5          5
         4         20
         3         60
         2        120
         1        120

and to display just the result, we select it from Factorial:

WITH Factorial (operand,total_so_far) AS
(SELECT 5 operand, 5 total_so_far FROM dual    -- Find the factorial of 5
UNION ALL
SELECT operand-1, total_so_far * (operand-1) FROM Factorial
WHERE operand > 1)
SELECT MAX(operand) || '! = ' || MAX(total_so_far) AS RESULT FROM Factorial;

RESULT
-----------------
5! = 120

Ahem! I have cheated a little for simplicity here. The query doesn’t take into account that Factorial(0) = 1:

WITH Factorial (operand,total_so_far) AS
(SELECT 0 operand, 0 total_so_far FROM dual    -- Find the factorial of 0
UNION ALL
SELECT operand-1, total_so_far * (operand-1) FROM Factorial
WHERE operand > 1)                             -- This is going to get me nowhere fast...
SELECT * FROM Factorial;

  OPERAND TOTAL_SO_F
---------- ----------
         0          0

To do it properly, we need to include Factorial(0) = 1 in the recursive subquery:

WITH Factorial (operand,total_so_far) AS
(SELECT 0 operand, 0 total_so_far FROM dual    -- Find the factorial of 0
UNION ALL
SELECT operand-1, 
CASE                                           -- Factorial (0) = 1
  WHEN operand=0 THEN 1
  ELSE (total_so_far * (operand-1))
  END
FROM Factorial
WHERE operand >= 0)
SELECT MAX(operand) || '! = ' || MAX(total_so_far) AS RESULT FROM Factorial;

RESULT
------------------------------------------------------------------------------------
0! = 1

We can also reverse direction and recursively build a table of factorials, multiplying as we go.
That’s the approach Lucas Jellema takes in his excellent blog post on factorials in SQL.

WITH Factorial (operand,output) AS
(SELECT 0 operand, 1 output FROM dual
UNION ALL
SELECT operand+1, output * (operand+1) FROM Factorial
WHERE operand < 5)
SELECT * FROM Factorial;

   OPERAND     OUTPUT
---------- ----------
         0          1
         1          1
         2          2
         3          6
         4         24
         5        120

There are two nice things about this approach: first, every row of the subquery result contains n and n! , and second, the rule that 0! = 1 is elegantly captured in the anchor member.

denrael ev’ew tahw gniylppA

Now let’s do something more interesting – reversing a string. Here’s some sample code in C from the CS 211 course at Cornell:

 public String reverseString(String word) {
     if(word == null || word.equals(""))
        return word;
     else
        return reverseString(word.substring(1, word.length())) + 
            word.substring(0,1);
  }

Let’s run through an example word to see how it works. For simplicity I’ll write reverseString(“word”) as r(word). Using “cat” as the word, stepping through the algorithm gives:

r(cat) = r(r(at))+c = r(r(r(t))+a+c = r(r(r(r())+t+a+c = ''+t+a+c = tac

Now to rewrite the same function in SQL. Using the same example string, “cat,” I want my recursively defined table to look like this:

in   out
--------
cat 
at   c
t    ac
     tac

In C, the initial letter in the word is the 0th letter, and in SQL, it’s the 1st letter. So the C expression word.substring(1,N) corresponds to SQL expression substr(word,2,N-1) . With that in mind, it’s easy to rewrite the C algorithm in SQL:

WITH WordReverse (INPUT, output) AS
  (SELECT 'CAT' INPUT, NULL output FROM dual
   UNION ALL
   SELECT substr(INPUT,2,LENGTH(INPUT)-1), substr(INPUT,1,1) || output
   FROM wordReverse
   WHERE LENGTH(INPUT) > 0
  )
SELECT * FROM wordReverse;

INPUT    OUTP
-------- ----
CAT
AT       C
T        AC
         TAC

NOTE: if using 11.2.0.3 or earlier, you might get “ORA-01489: result of string concatenation is too long” when reversing anything longer than a few letters. This is due to Bug 13876895: False ora-01489 on recursive WITH clause when concatenating columns. The bug is fixed in 11.2.0.4 and 12.1.0.1, and there’s an easy workaround: Cast one of the inputs to the concatenation as a varchar2(4000).

We could make this query user-friendlier by using a sql*plus variable to hold the input string. Another approach is to add an additional subquery to the with block to “pass in” parameters. I picked this up from Lucas Jellema’s post mentioned above, and wanted to give it a try, so I’ll add it in to my WordReverse query here.

Let’s use this to reverse a word that’s really quite atrocious:

WITH
params AS
  (SELECT 'supercalifragilisticexpialidocious' phrase FROM dual),
WordReverse (inpt, outpt) AS
  (SELECT phrase inpt, CAST(NULL AS varchar2(4000)) outpt FROM params
   UNION ALL
   SELECT substr(inpt,2,LENGTH(inpt)-1), substr(inpt,1,1) || outpt
   FROM wordReverse
   WHERE LENGTH(inpt) > 0
  )
SELECT phrase,outpt AS reversed FROM wordReverse, params
WHERE LENGTH(outpt) = LENGTH(phrase) ;

PHRASE                             REVERSED
---------------------------------- ----------------------------------------
supercalifragilisticexpialidocious suoicodilaipxecitsiligarfilacrepus

Now you might not have needed to know how to spell “supercalifragilisticexpialidocious” backwards, but one recursive requirement that does come up often is querying hierarchical data. I wrote a series of posts on hierarchical data recently, using Oracle’s CONNECT BY syntax. But recursive WITH can also be used to query hierarchical data. That’ll be the subject of my next post.


Republished with permission. Original URL: http://rdbms-insight.com/wp/?p=94




ive

Recursive WITH, part II: Hierarchical queries

articles: 

In my last post, I looked at using recursive WITH to implement simple recursive algorithms in SQL. One very common use of recursion is to traverse hierarchical data. I recently wrote a series of posts on hierarchical data, using Oracle’s CONNECT BY syntax and a fun example. In this post, I’ll be revisiting the same data using recursive WITH.

There are dozens of examples of hierarchical data, from the EMP table to the Windows Registry to binary trees, but I went with something more fun: the skeleton from the old song “Dem Dry Bones”.

Quote:
Toe bone connected to the foot bone
Foot bone connected to the heel bone
Heel bone connected to the ankle bone
Ankle bone connected to the shin bone
Shin bone connected to the knee bone
Knee bone connected to the thigh bone
Thigh bone connected to the hip bone
Hip bone connected to the back bone
Back bone connected to the shoulder bone
Shoulder bone connected to the neck bone
Neck bone connected to the head bone

Since every bone has only one ancestor, and there is a root bone with no ancestor, this is hierarchical data and we can stick it in a table and query it.

SELECT * FROM skeleton;
BONE                                     CONNECTED_TO_THE
---------------------------------------- ----------------------------------------
shoulder                                 neck
back                                     shoulder
hip                                      back
thigh                                    hip
knee                                     thigh
leg                                      knee
foot                                     heel
head
neck                                     head
toe                                      foot
arm                                      shoulder
wrist                                    arm
ankle                                    leg
heel                                     ankle
finger                                   wrist
a rib                                    back
b rib                                    back
c rib                                    back

You can see that I added some ribs and an arm to make the skeleton more complete!

Using Oracle’s CONNECT BY syntax:

SQL> col bone FOR a10
SQL> col connected_to_the FOR a9
SQL> col level FOR 99
SQL> col bone_tree FOR a27
SQL> col path FOR a65
 
SELECT bone, connected_to_the, level, 
lpad(' ',2*level, ' ') || bone AS bone_tree , 
ltrim(sys_connect_by_path(bone,'>'),'>') AS path
FROM skeleton
START WITH connected_to_the IS NULL
CONNECT BY prior bone=connected_to_the 
ORDER siblings BY 1

BONE       CONNECTED LEVEL BONE_TREE                   PATH
---------- --------- ----- --------------------------- -----------------------------------------------------------------
head                     1   head                      head
neck       head          2     neck                    head>neck
shoulder   neck          3       shoulder              head>neck>shoulder
arm        shoulder      4         arm                 head>neck>shoulder>arm
wrist      arm           5           wrist             head>neck>shoulder>arm>wrist
finger     wrist         6             finger          head>neck>shoulder>arm>wrist>finger
back       shoulder      4         back                head>neck>shoulder>back
a rib      back          5           a rib             head>neck>shoulder>back>a rib
b rib      back          5           b rib             head>neck>shoulder>back>b rib
c rib      back          5           c rib             head>neck>shoulder>back>c rib
hip        back          5           hip               head>neck>shoulder>back>hip
thigh      hip           6             thigh           head>neck>shoulder>back>hip>thigh
knee       thigh         7               knee          head>neck>shoulder>back>hip>thigh>knee
leg        knee          8                 leg         head>neck>shoulder>back>hip>thigh>knee>leg
ankle      leg           9                   ankle     head>neck>shoulder>back>hip>thigh>knee>leg>ankle
heel       ankle        10                     heel    head>neck>shoulder>back>hip>thigh>knee>leg>ankle>heel
foot       heel         11                       foot  head>neck>shoulder>back>hip>thigh>knee>leg>ankle>heel>foot
toe        foot         12                         toe head>neck>shoulder>back>hip>thigh>knee>leg>ankle>heel>foot>toe

The above CONNECT BY query uses the LEVEL pseudocolumn and the SYS_CONNECT_BY_PATH function. With recursive WITH, there’s no need for these built-ins because these values fall naturally out of the recursion.

Let’s start with the basic hierarchical query rewritten in recursive WITH.
The hierarchical relationship in our table is:
Parent(row.bone) = row.connected_to_the

WITH skellarchy (bone, parent) AS
 ( SELECT bone, connected_to_the FROM skeleton 
   WHERE bone = 'head'                         -- Start with the root
 UNION ALL
   SELECT s.bone, s.connected_to_the 
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           -- Parent(row.bone) = row.connected_to_the
 )
SELECT * FROM skellarchy;

BONE       PARENT
---------- ----------------------------------------
head
neck       head
shoulder   neck
back       shoulder
arm        shoulder
hip        back
wrist      arm
a rib      back
b rib      back
c rib      back
thigh      hip
finger     wrist
knee       thigh
leg        knee
ankle      leg
heel       ankle
foot       heel
toe        foot

Because we built up the SKELLARCHY table recursively, it’s easy to make an equivalent to the LEVEL pseudocolumn; it falls right out of the recursion:

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0 FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SELECT * FROM skellarchy;

BONE       PARENT      THE_LEVEL
---------- ---------- ----------
head                           0
neck       head                1
shoulder   neck                2
back       shoulder            3
arm        shoulder            3
hip        back                4
wrist      arm                 4
a rib      back                4
b rib      back                4
c rib      back                4
thigh      hip                 5
finger     wrist               5
knee       thigh               6
leg        knee                7
ankle      leg                 8
heel       ankle               9
foot       heel               10
toe        foot               11

and it’s also easy to build up a path from root to the current node like the “SYS_CONNECT_BY_PATH” function does for CONNECT BY queries:

WITH skellarchy (bone, parent, the_level, the_path) AS
 ( SELECT bone, connected_to_the, 0, CAST(bone AS varchar2(4000)) FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1, r.the_path || '->' || s.bone
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SELECT * FROM skellarchy;

BONE       PARENT     THE_LEVEL THE_PATH
---------- ---------- --------- --------------------------------------------------------------------------------
head                          0 head
neck       head               1 head->neck
shoulder   neck               2 head->neck->shoulder
back       shoulder           3 head->neck->shoulder->back
arm        shoulder           3 head->neck->shoulder->arm
hip        back               4 head->neck->shoulder->back->hip
wrist      arm                4 head->neck->shoulder->arm->wrist
a rib      back               4 head->neck->shoulder->back->a rib
b rib      back               4 head->neck->shoulder->back->b rib
c rib      back               4 head->neck->shoulder->back->c rib
thigh      hip                5 head->neck->shoulder->back->hip->thigh
finger     wrist              5 head->neck->shoulder->arm->wrist->finger
knee       thigh              6 head->neck->shoulder->back->hip->thigh->knee
leg        knee               7 head->neck->shoulder->back->hip->thigh->knee->leg
ankle      leg                8 head->neck->shoulder->back->hip->thigh->knee->leg->ankle
heel       ankle              9 head->neck->shoulder->back->hip->thigh->knee->leg->ankle->heel
foot       heel              10 head->neck->shoulder->back->hip->thigh->knee->leg->ankle->heel->foot
toe        foot              11 head->neck->shoulder->back->hip->thigh->knee->leg->ankle->heel->foot->toe

and we can use our generated the_level column to make a nice display just as we used the level pseudocolumn with CONNECT BY:

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0  FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree FROM skellarchy;

BONE_TREE
---------------------------
head
  neck
    shoulder
      back
      arm
        hip
        wrist
        a rib
        b rib
        c rib
          thigh
          finger
            knee
              leg
                ankle
                  heel
                    foot
                      toe

Now, the bones are coming out in a bit of a funny order for a skeleton. Instead of this:

    shoulder
      back
      arm
        hip
        wrist
        a rib
        b rib
        c rib
          thigh
          finger

I want to see this:

    shoulder
      arm
        wrist
          finger
      back
        a rib
        b rib
        c rib
        hip
          thigh

The rows are coming out in BREADTH FIRST ordering – meaning all siblings of ‘shoulder’ are printed before any children of ‘shoulder’. But I want to see them in DEPTH FIRST: going from shoulder to finger before we start on the backbone.

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0  FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SEARCH DEPTH FIRST BY bone SET bone_order
SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree FROM skellarchy
ORDER BY bone_order;

BONE_TREE
---------------------------
head
  neck
    shoulder
      arm
        wrist
          finger
      back
        a rib
        b rib
        c rib
        hip
          thigh
            knee
              leg
                ankle
                  heel
                    foot
                      toe

And now the result looks more like a proper skeleton.

Now on to cycles. A cycle is a loop in the hierarchical data: a row is its own ancestor. To put a cycle in the example data, I made the skeleton bend over and connect the head to the toe:

UPDATE skeleton SET connected_to_the='toe' WHERE bone='head';

And now if we try to run the query:

ERROR at line 2:
ORA-32044: cycle detected while executing recursive WITH query

With the CONNECT BY syntax, we can use CONNECT BY NOCYCLE to run a query even when cycles exist, and the pseudocolumn CONNECT_BY_IS_CYCLE to help detect cycles. For recursive WITH, Oracle provides a CYCLE clause, which is a bit more powerful as it allows us to name the column which is cycling.

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0  FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SEARCH DEPTH FIRST BY bone SET bone_order
CYCLE bone SET is_a_cycle TO 'Y' DEFAULT 'N'
SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree, is_a_cycle FROM skellarchy
--where is_a_cycle='N'
ORDER BY bone_order;

BONE_TREE                                                    I
------------------------------------------------------------ -
head                                                         N
  neck                                                       N
    shoulder                                                 N
      arm                                                    N
        wrist                                                N
          finger                                             N
      back                                                   N
        a rib                                                N
        b rib                                                N
        c rib                                                N
        hip                                                  N
          thigh                                              N
            knee                                             N
              leg                                            N
                ankle                                        N
                  heel                                       N
                    foot                                     N
                      toe                                    N
                        head                                 Y

The query runs until the first cycle is detected, then stops.

The CONNECT BY syntax does provide a nice pseudocolumn, CONNECT_BY_ISLEAF, which is 1 when a row has no further children, 0 otherwise. In my next post, I’ll look at emulating this pseudocolumn with recursive WITH.


Republished with permission. Original URL: http://rdbms-insight.com/wp/?p=103




ive

Recursive WITH, part III: IS_LEAF

articles: 

The CONNECT BY syntax provides a useful pseudocolumn, CONNECT_BY_ISLEAF, which identifies leaf nodes in the data: it’s 1 when a row has no further children, 0 otherwise. In this post, I’ll look at emulating this pseudocolumn using recursive WITH.

Let’s continue with the example from my previous posts about hierarchical data: the skeleton from the old song “Dem Dry Bones”.

UPDATE skeleton SET connected_to_the=NULL WHERE bone='head';
SELECT * FROM skeleton;

BONE                                     CONNECTED_TO_THE
---------------------------------------- ----------------------------------------
shoulder                                 neck
back                                     shoulder
hip                                      back
thigh                                    hip
knee                                     thigh
leg                                      knee
foot                                     heel
head
neck                                     head
toe                                      foot
arm                                      shoulder
wrist                                    arm
ankle                                    leg
heel                                     ankle
finger                                   wrist
a rib                                    back
b rib                                    back
c rib                                    back

With CONNECT BY, we can use the CONNECT_BY_ISLEAF pseudocolumn to identify leaf nodes:

SELECT bone, level, 
ltrim(sys_connect_by_path(bone,' -> '),' -> ') AS path
FROM skeleton
WHERE connect_by_isleaf=1
START WITH connected_to_the IS NULL
CONNECT BY prior bone=connected_to_the 
ORDER siblings BY 1;

BONE      LEVEL PATH                                                                                            
--------- ----- ----------------------------------------------------------------------------------------------- 
finger        6 head -> neck -> shoulder -> arm -> wrist -> finger                                              
a rib         5 head -> neck -> shoulder -> back -> a rib                                                       
b rib         5 head -> neck -> shoulder -> back -> b rib                                                       
c rib         5 head -> neck -> shoulder -> back -> c rib                                                       
toe          12 head -> neck -> shoulder -> back -> hip -> thigh -> knee -> leg -> ankle -> heel -> foot -> toe

This pseudocolumn takes a little more thought to replicate using recursive WITH than the LEVEL pseudocolumn and the SYS_CONNECT_BY_PATH, which, as we saw in my last post, fall naturally out of the recursion.

We can imitate CONNECT_BY_ISLEAF by searching DEPTH FIRST and using the LEAD function to peek at the next row’s the_level value. If the next row’s level is higher than the current row, then it’s a child of the current row; otherwise, it’s not a child. Since, with DEPTH FIRST, all the children of a row come out before any siblings, if the next row isn’t a child, then the current row is a leaf.

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0  FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SEARCH DEPTH FIRST BY bone SET bone_order
CYCLE bone SET is_a_cycle TO 'Y' DEFAULT 'N'
SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree , the_level,
  lead(the_level) OVER (ORDER BY bone_order) AS next_level,
  CASE 
    WHEN the_level < lead(the_level) OVER (ORDER BY bone_order) THEN NULL
    ELSE 'LEAF'
  END is_leaf
FROM skellarchy
ORDER BY bone_order;

BONE_TREE                                      THE_LEVEL NEXT_LEVEL IS_L
--------------------------------------------- ---------- ---------- ----
head                                                   0          1
  neck                                                 1          2
    shoulder                                           2          3
      arm                                              3          4
        wrist                                          4          5
          finger                                       5          3 LEAF
      back                                             3          4
        a rib                                          4          4 LEAF
        b rib                                          4          4 LEAF
        c rib                                          4          4 LEAF
        hip                                            4          5
          thigh                                        5          6
            knee                                       6          7
              leg                                      7          8
                ankle                                  8          9
                  heel                                 9         10
                    foot                              10         11
                      toe                             11            LEAF

Watch out for Cycles

The first point of caution about this solution concerns cycles. In my last post, I had created a cycle by making the ‘head’ node’s parent the ‘toe’ node. If I’d left the cycle in the data, the toe node wouldn’t be a leaf any more, but this query would falsely identify the head as a leaf:

UPDATE skeleton SET connected_to_the='toe' WHERE bone='head';

BONE_TREE                                      THE_LEVEL NEXT_LEVEL IS_L
--------------------------------------------- ---------- ---------- ----
head                                                   0          1
  neck                                                 1          2
    shoulder                                           2          3
      arm                                              3          4
        wrist                                          4          5
          finger                                       5          3 LEAF
      back                                             3          4
        a rib                                          4          4 LEAF
        b rib                                          4          4 LEAF
        c rib                                          4          4 LEAF
        hip                                            4          5
          thigh                                        5          6
            knee                                       6          7
              leg                                      7          8
                ankle                                  8          9
                  heel                                 9         10
                    foot                              10         11
                      toe                             11         12
                        head                          12            LEAF
 
19 rows selected.

This can be corrected for by adding WHERE IS_A_CYCLE=’N’ to the query.

Respect the order of evaluation…

A second point of caution: if I add a WHERE clause to the query that limits the number of levels, the last line of the resultset will always be identified as a leaf.

WITH skellarchy (bone, parent, the_level) AS
 ( SELECT bone, connected_to_the, 0  FROM skeleton 
   WHERE bone = 'head'                         
 UNION ALL
   SELECT s.bone, s.connected_to_the , r.the_level + 1
   FROM skeleton s, skellarchy r
   WHERE r.bone = s.connected_to_the           
 )
SEARCH DEPTH FIRST BY bone SET bone_order
CYCLE bone SET is_a_cycle TO 'Y' DEFAULT 'N'
SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree , the_level,
  lead(the_level) OVER (ORDER BY bone_order) AS next_level,
  CASE 
    WHEN the_level < lead(the_level) OVER (ORDER BY bone_order) THEN NULL
    ELSE 'LEAF'
  END is_leaf
FROM skellarchy
WHERE the_level < 8 
ORDER BY bone_order;

BONE_TREE                                                     THE_LEVEL NEXT_LEVEL IS_L
------------------------------------------------------------ ---------- ---------- ----
head                                                                  0          1
  neck                                                                1          2
    shoulder                                                          2          3
      arm                                                             3          4
        wrist                                                         4          5
          finger                                                      5          3 LEAF
      back                                                            3          4
        a rib                                                         4          4 LEAF
        b rib                                                         4          4 LEAF
        c rib                                                         4          4 LEAF
        hip                                                           4          5
          thigh                                                       5          6
            knee                                                      6          7
              leg                                                     7            LEAF      <<<=====

The leg is falsely identified as a leaf, and NEXT_LEVEL comes out as NULL, even though the ‘leg’ row has a child row. Why is that? It’s because this solution uses the LEAD analytic function. With analytic functions, WHERE clauses are evaluated before the analytic functions.

Highlighting the relevant bits from the query:

WITH skellarchy AS ...[recursive WITH subquery]...
SELECT ... LEAD(the_level) OVER (ORDER BY bone_order) AS next_level ... --analytic function
FROM skellarchy
WHERE the_level < 8 ...                                                 --where clause

To quote the documentation:

Analytic functions compute an aggregate value based on a group of rows…. The group of rows is called a window and is defined by the analytic_clause. For each row, a sliding window of rows is defined. The window determines the range of rows used to perform the calculations for the current row…. Analytic functions are the last set of operations performed in a query except for the final ORDER BY clause. All joins and all WHERE, GROUP BY, and HAVING clauses are completed before the analytic functions are processed.

In the query above, “where the_level < 8" will be evaluated before LEAD(the_level). The EXPLAIN PLAN shows this very clearly:

-----------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |          |     2 |    76 |     8  (25)| 00:00:01 |
|   1 |  WINDOW BUFFER                           |          |     2 |    76 |     8  (25)| 00:00:01 |  <<=== LEAD
|*  2 |   VIEW                                   |          |     2 |    76 |     8  (25)| 00:00:01 |  <<=== filter("THE_LEVEL"<8)
|   3 |    UNION ALL (RECURSIVE WITH) DEPTH FIRST|          |       |       |            |          |
|*  4 |     TABLE ACCESS FULL                    | SKELETON |     1 |    24 |     2   (0)| 00:00:01 |
|*  5 |     HASH JOIN                            |          |     1 |    49 |     5  (20)| 00:00:01 |
|   6 |      RECURSIVE WITH PUMP                 |          |       |       |            |          |
|   7 |      TABLE ACCESS FULL                   | SKELETON |    18 |   432 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("THE_LEVEL"<8)
   4 - filter("BONE"='head')
   5 - access("R"."BONE"="S"."CONNECTED_TO_THE")

The WINDOW BUFFER (analytic window) is evaluated after the VIEW which filters on “THE_LEVEL”<8. So, "lead(the_level) over (order by bone_order)" will be null where the_level=7, and the 'leg' wrongly identified as a leaf node. What we actually want is for the analytic function LEAD to run over the whole resultset, and only then limit the results to show the levels 0-7. The obvious way to do this is to wrap the query in a second SELECT statement:

SELECT * FROM (
  WITH skellarchy (bone, parent, the_level) AS
   ( SELECT bone, connected_to_the, 0  FROM skeleton 
     WHERE bone = 'head'                         
   UNION ALL
     SELECT s.bone, s.connected_to_the , r.the_level + 1
     FROM skeleton s, skellarchy r
     WHERE r.bone = s.connected_to_the           
   )
  SEARCH DEPTH FIRST BY bone SET bone_order
  CYCLE bone SET is_a_cycle TO 'Y' DEFAULT 'N'
  SELECT lpad(' ',2*the_level, ' ') || bone AS bone_tree , the_level,
    lead(the_level) OVER (ORDER BY bone_order) AS next_level,
    CASE 
      WHEN the_level < lead(the_level) OVER (ORDER BY bone_order) THEN NULL
      ELSE 'LEAF'
    END is_leaf
  FROM skellarchy
  ORDER BY bone_order
) WHERE the_level < 8;

BONE_TREE                                                     THE_LEVEL NEXT_LEVEL IS_L
------------------------------------------------------------ ---------- ---------- ----
head                                                                  0          1
  neck                                                                1          2
    shoulder                                                          2          3
      arm                                                             3          4
        wrist                                                         4          5
          finger                                                      5          3 LEAF
      back                                                            3          4
        a rib                                                         4          4 LEAF
        b rib                                                         4          4 LEAF
        c rib                                                         4          4 LEAF
        hip                                                           4          5
          thigh                                                       5          6
            knee                                                      6          7
              leg                                                     7          8

Now, the analytic function in the inner query is evaluated first, before the WHERE clause in the outer query. We can see this in the EXPLAIN PLAN too, of course. Now the WINDOW BUFFER (analytic window) is evaluated before the VIEW with filter(“THE_LEVEL”<8) :

------------------------------------------------------------------------------------------------------
| Id  | Operation                                 | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                          |          |     2 |  4068 |     8  (25)| 00:00:01 |
|*  1 |  VIEW                                     |          |     2 |  4068 |     8  (25)| 00:00:01 |  <<=== filter("THE_LEVEL"<8)
|   2 |   WINDOW BUFFER                           |          |     2 |    76 |     8  (25)| 00:00:01 |  <<=== LEAD
|   3 |    VIEW                                   |          |     2 |    76 |     8  (25)| 00:00:01 |
|   4 |     UNION ALL (RECURSIVE WITH) DEPTH FIRST|          |       |       |            |          |
|*  5 |      TABLE ACCESS FULL                    | SKELETON |     1 |    24 |     2   (0)| 00:00:01 |
|*  6 |      HASH JOIN                            |          |     1 |    49 |     5  (20)| 00:00:01 |
|   7 |       RECURSIVE WITH PUMP                 |          |       |       |            |          |
|   8 |       TABLE ACCESS FULL                   | SKELETON |    18 |   432 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("THE_LEVEL"<8)
   5 - filter("BONE"='head')
   6 - access("R"."BONE"="S"."CONNECTED_TO_THE")

This is one case of the general point that, as Tom Kyte explains in this Ask Tom answer,“select analytic_function from t where CONDITION” is NOT THE SAME AS “select * from (select analytic_function from t) where CONDITION”.

So, to sum up my last few posts, we can do everything that CONNECT BY can do with the 11g recursive WITH syntax. Plus, the recursive WITH syntax makes it easy to express simple recursive algorithms in SQL.


Republished with permission. Original URL: http://rdbms-insight.com/wp/?p=135




ive

Monthly Blank T-Shirt Delivery

Monthly Blank T-Shirt Delivery to you or your family/customers home. This is a monthly delivery service; you can cancel at any time. You can use the Contact Us form to make cancellation requests.



  • Apparel
  • Monthly Blank T-Shirt Delivery

ive

The universe on your wrist…Jacob & Co.

Now if you ever wanted to be the centre of the universe this watch would definitely help. The huge Jacob & Co. Astronomia measuring in at 43.4mm is opulence at its best. Encased in a rose gold case and powered by the exclusive Jacob & Co. , the manually-wound JCAM19 calibre watch operates at 28,800...




ive

Ukraine-Liveblog: ++ Kiew: Russland plant Angriff an Südfront ++

Russland bereitet ukrainischen Angaben zufolge einen Angriff an der südlichen Front vor. US-Außenminister Blinken reist am Mittwoch nach Brüssel, um über die Lage in der Ukraine zu beraten. Die Entwicklungen vom Dienstag zum Nachlesen.




ive

Nahost-Liveblog: ++ US-Militärhilfe wird nicht eingeschränkt ++

Die USA werden ihre Militärhilfe für Israel nicht einschränken. Washington hatte dies vor einem Monat angedroht. Israel will offenbar erneut Einberufungsbefehle für Ultraorthodoxe ausstellen. Die Entwicklungen vom Dienstag zum Nachlesen.




ive

Liveblog zu Neuwahlen: ++ Kukies erwartet keine Haushaltssperre ++

Der neue Bundesfinanzminister Kukies geht nicht davon aus, dass es eine Haushaltssperre geben wird. BSW-Chefin Wagenknecht sieht für ihre Partei durch den Zeitdruck Herausforderungen. Die Entwicklungen vom Dienstag zum Nachlesen.




ive

Nahost-Liveblog: ++ USA greifen Milizen im Jemen und Syrien an ++

Das US-Militär hat Stellungen proiranischer Milizen im Jemen und in Syrien angegriffen. Nach Angaben des UN-Menschenrechtsbüros sind rund 70 Prozent der Getöteten im Gazastreifen Kinder und Frauen. Die Entwicklungen im Liveblog.




ive

Liveblog zu Neuwahlen: ++ Merz fordert grundlegende Politikwende ++

CDU-Chef Merz hat die Regierungserklärung des Bundeskanzlers scharf kritisiert und sich für ein Umsteuern in der deutschen Politik ausgesprochen. Scholz bestätigte den Zeitplan für die Vertrauensfrage. Die Entwicklungen im Liveblog.




ive

Ukraine-Liveblog: ++ cheidende US-Regierung will Ukrainehilfe verstärken ++

Die scheidende US-Regierung will laut Außenminister Blinken die Ukraine noch im vollen Umfang unterstützen. Kiew wird erstmals seit August von den russischen Streitkräften mit Raketen beschossen. Die Entwicklungen im Liveblog.