CTE to create Tree of objects - recursion

I have two tables:
Folder, Files.
Each table has 4 fields:
ParentID, ID, Name, Type
I need to create a CTE in order to retrieve the entire tree in one shot, using T-SQL.
So far this is what I have done, but the recursion is not active yet.
I forgot to mention that inside a folder there may be one or more folders/files.

Assuming that you're using NULL for the ParentID items in the root the following should work:
WITH FilesAndFolders AS (
SELECT ID AS FileID,
CAST(NULL AS int) AS FolderID,
ParentID,
Name,
[Type]
FROM File
UNION ALL
SELECT CAST(NULL AS int),
ID,
ParentID,
Name,
[Type]
FROM Folder
),
Tree AS (
SELECT FileID,
FolderID,
Name,
[Type],
CAST('' AS nvarchar(MAX)) AS [Path]
FROM FilesAndFolders
WHERE ParentID IS NULL
UNION ALL
SELECT FF.FileID,
FF.FolderID,
FF.Name,
FF.[Type],
T.[Path]+T.[Name]+'/'
FROM FilesAndFolders FF
JOIN Tree T ON T.FolderID = FF.ParentID
)
SELECT FileID, FolderID, [Path]+[Name] FullName, [Type]
FROM Tree

Related

The default schema does not exist. error when executing stored procedure

Create stored procedure
CREATE PROCEDURE dbo.GetBookList
-- Add the parameters for the stored procedure here
AS
BEGIN
create table BookAuthersName
(
BookId int,
Names varchar(255)
);
insert into BookAthersName(BookId,Names)
select
t1.BookId,
stuff((select ', '+a.Name
from BookAuthers t2 join Authers a on t2.AutherId= a.Id where t1.BookId = t2.BookId
for xml path('')),
1,2,'') [Values]
from dbo.BookAuthers t1
group by t1.BookId
create table BookSubjectNames
(
BookTypeId int,
Names varchar(255)
);
insert into BookSubjectNames(BookTypeId,Names)
select
t1.BookTypeId,
stuff((select ', '+a.Name
from BookTypeSubjects t2 join Subjects a on t2.SubjectId= a.Id where t1.BookTypeId = t2.BookTypeId
for xml path('')),
1,2,'') [Values]
from dbo.BookTypeSubjects t1
group by t1.BookTypeId
SELECT dbo.BooksType.Name, dbo.BooksType.BuyingDate AS [Buying Date], dbo.Publishers.Name AS [Publisher Name], dbo.Inventory.TotalBooks AS [Total Books],
dbo.Inventory.TotalIssuedBooks AS [Total Issued Books], ban.Names as [Auther Names] ,bsn.Names as [Subject Names]
FROM dbo.BooksType INNER JOIN dbo.Inventory
ON dbo.BooksType.Id = dbo.Inventory.BookTypeId
INNER JOIN dbo.Publishers ON dbo.BooksType.PublisherId = dbo.Publishers.Id
inner join BookAuthersName ban on dbo.BooksType.Id = ban .BookId
inner join BookSubjectNames bsn on dbo.BooksType.Id = bsn .BookTypeId
drop table BookAuthersName
drop table BookSubjectNames
END
It gives error when executing through a .net website. Error is
The default schema does not exist. error when executing stored
procedure.
Gone through some solutions but none seems to help
I am using Integrated Security=True in webconfig connection string
First thing you must get schema name with query
select schema_name()
If the schema name is null you must try set default name with query
ALTER USER [dbo.database_name] WITH DEFAULT_SCHEMA = [dbo];

How to reuse a table with UNION?

I am trying to reuse a table in SQLite. My attempt is as follows:
SELECT
Partials.e_sentence
FROM
(SELECT
e_sentence, _id
FROM
Pair
JOIN PairCategories
ON
_id=PairId AND CategoryId=53
UNION
SELECT
e_sentence, _id
FROM
Pair
WHERE
e_sentence LIKE '%' || 'how often' || '%'
GROUP BY
e_sentence)
AS Parents JOIN Partials
ON Parents._id=ParentId
UNION
SELECT
e_sentence
FROM
Parents
The key part I am trying to accomplish is at the bottom, where I try to UNION a table created in the previous statement. Is there a way to do this in SQLite, or am I forced to repeat the query that made the Parents table in the first half of the UNION?
In SQLite 3.8.3 or later, you can use a common table expression:
WITH Parents AS (
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...
)
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you're using an older SQLite (probably because you're using an older Android), you can create a view for the subquery:
CREATE VIEW Parents AS
SELECT e_sentence, _id
FROM Pair
JOIN PairCategories
...;
SELECT Partials.e_sentence
FROM Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM Parents;
If you do not want to have this view permanently in the database, you could make it temporary (CREATE TEMPORARY VIEW ...) so that it is not available outside the current database connection, or, as last resort, you could just insert the subquery wherever you would use Parent:
SELECT Partials.e_sentence
FROM (SELECT ...) AS Parents
JOIN Partials ON Parents._id = ParentId
UNION
SELECT e_sentence
FROM (SELECT ...) AS Parents;

sqlite query performance using nested queries

I use the following sqlite query in c#. The query is iterated for 100K items. I feel the execution is slow and by optimizing the query the speed can be increased
insert into salesmetrics(salesid, salesrankcount, volumerankcount, countsales,Avgsales)
select #salesid,sum(salesrank), sum(volumerank), Count(salesrank), avg(salesrank)
from (select salesrank, volumerank
from salesindex
Join SalesData on salesindex.salesindexID = salesData.salesindexID
where SourceID = #Sourceid
and Content like #content
group by salesindex.salesindexID)
Tables:
CREATE TABLE `salesindex` (
salesindexID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
salesindex TEXT,
SourceID INTEGER,
salesrank INTEGER,
volumerank INTEGER,
dateAdded DATETIME,
UNIQUE(SourceID,dateAdded)
);
CREATE TABLE `SalesData` (
SalesDataID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
salesindexID INTEGER,
Content TEXT,
dateAdded DATETIME,
UNIQUE(salesindexID,content)
);
CREATE TABLE `salesmetrics` (
salesmetricsID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
salesID INTEGER,
SalesRankCount INTEGER,
VolumeRankCount INTEGER,
countsales INTEGER,
SalesRank FLOAT
);
Index on "content" on salesdata table and sourceid on salesindex table
The main problem is the GROUP BY in the inner query, which is apparently needed to squash multiple matching SalesData rows.
Do the Content check in a subquery instead:
SELECT #salesid,
SUM(salesrank),
SUM(volumerank),
COUNT(salesrank),
AVG(salesrank)
FROM salesindex
WHERE salesindexID IN (SELECT salesindexID
FROM SalesData
WHERE Content LIKE #content)
AND SourceID = #Sourceid;
Alternatively, depending on the selectivity of the Content lookup, it might be a better idea to do the lookup as a correlated subquery:
SELECT #salesid,
SUM(salesrank),
SUM(volumerank),
COUNT(salesrank),
AVG(salesrank)
FROM salesindex
WHERE EXISTS (SELECT 1
FROM SalesData
WHERE salesindexID = salesindex.salesindexID
AND Content LIKE #content)
AND SourceID = #Sourceid;
Unless I'm missing something, I think you could insert directly the select, instead of the select of the select:
insert into salesmetrics(salesid, salesrankcount, volumerankcount, countsales,Avgsales)
(select #salesid,sum(salesrank), sum(volumerank), Count(salesrank), avg(salesrank)
from salesindex
Join SalesData on salesindex.salesindexID = salesData.salesindexID
where SourceID = #Sourceid
and Content like #content
group by salesindex.salesindexID)

Refactor SQLite Table by splitting it in two and link with foreign keys

I'm working on a SQLite Database. The database is already filled, but I want to refactor it. Here is a sample of what I need to do:
I currently have one table:
CREATE TABLE Cars (ID INTEGER PRIMARY KEY,
Name VARCHAR(32),
TopSpeed FLOAT,
EngineCap FLOAT);
I want to split this into two tables:
CREATE TABLE Vehicles (ID INTEGER PRIMARY KEY,
Name VARCHAR(32),
TopSpeed FLOAT);
CREATE TABLE Cars (ID INTEGER PRIMARY KEY,
VehicleID INTEGER CONSTRAINT FK_Cars REFERENCES [Vehicles](ID),
EngineCap FLOAT);
I have figured out to create a temporary table with the Cars table contents, and I can fill up the Vehicles table with the contents of the Cars table:
CREATE TEMPORARY TABLE Cars_temp AS SELECT * FROM Cars;
INSERT INTO Vehicles (Name, TopSpeed)
SELECT Name, TopSpeed FROM Cars_temp;
But I am still looking for a way to go over that same selection, while putting the EngineCap field into the new Cars table and somehow extracting the corresponding ID value from the Vehicles table to put into the VehicleID foreign key field on the Cars table.
I'm open for workaround or alternative approaches.
Thanks.
Since #mateusza did not provide an example, I've made one:
Suppose you have this table:
CREATE TABLE [Customer] (
[name] TEXT,
[street] TEXT,
[city] TEXT);
Now you want to move street and city into a separate table Address, so you'll end up with two tables:
CREATE TABLE [Customer2] (
[name] TEXT,
[addr] INTEGER);
CREATE TABLE [Address] (
[rowid] INTEGER NOT NULL,
[street] TEXT,
[city] TEXT,
PRIMARY KEY ([rowid])
);
(For this example, I'm doing the conversion in the same database. You'd probably use two DBs, converting one into the other, with an SQL ATTACH command.)
Now we create a view (which imitates our original table using the new tables) and the trigger:
CREATE VIEW Customer1 (name, street, city) AS
SELECT C.name, A.street, A.city FROM Customer2 AS C
JOIN Address as A ON (C.addr == A.rowid);
CREATE TEMP TRIGGER TempTrig INSTEAD OF INSERT ON Customer1 FOR EACH ROW BEGIN
INSERT INTO Address (street, city) SELECT NEW.street, NEW.city;
INSERT INTO Customer2 (addr, name) SELECT last_insert_rowid(), NEW.name;
END;
Now you can copy the table rows:
INSERT INTO Customer1 (name, street, city) SELECT name, street, city FROM Customer;
The above is a simplified case where you'd only move some data into a single new table.
A more complex (and more general) case is where you want to...
Separate your original table's columns into several foreign tables, and
Have unique entries in the foreign tables (that's usually the reason why you'd refactor your table).
This adds some additional challenges:
You'll end up inserting into multiple tables before you can insert their rowids into the table with the referencing rowids. This requires storing the results of each INSERT's last_insert_rowid() into a temporary table.
If the value already exists in the foreign table, its rowid must be stored instead of the one from the (non-executed) insertion operation.
Here's a complete solution for this. It manages a database of music records, constisting of a song's name, album title and artist name.
-- Original table
CREATE TABLE [Song] (
[title] TEXT,
[album] TEXT,
[artist] TEXT
);
-- Refactored tables
CREATE TABLE [Song2] (
[title] TEXT,
[album_rowid] INTEGER,
[artist_rowid] INTEGER
);
CREATE TABLE [Album] (
[rowid] INTEGER PRIMARY KEY AUTOINCREMENT,
[title] TEXT UNIQUE
);
CREATE TABLE [Artist] (
[rowid] INTEGER PRIMARY KEY AUTOINCREMENT,
[name] TEXT UNIQUE
);
-- Fill with sample data
INSERT INTO Song VALUES ("Hunting Girl", "Songs From The Wood", "Jethro Tull");
INSERT INTO Song VALUES ("Acres Wild", "Heavy Horses", "Jethro Tull");
INSERT INTO Song VALUES ("Broadford Bazar", "Heavy Horses", "Jethro Tull");
INSERT INTO Song VALUES ("Statue of Liberty", "White Music", "XTC");
INSERT INTO Song VALUES ("Standing In For Joe", "Wasp Star", "XTC");
INSERT INTO Song VALUES ("Velvet Green", "Songs From The Wood", "Jethro Tull");
-- Conversion starts here
CREATE TEMP TABLE [TempRowIDs] (
[album_id] INTEGER,
[artist_id] INTEGER
);
CREATE VIEW Song1 (title, album, artist) AS
SELECT Song2.title, Album.title, Artist.name
FROM Song2
JOIN Album ON (Song2.album_rowid == Album.rowid)
JOIN Artist ON (Song2.artist_rowid == Artist.rowid);
CREATE TEMP TRIGGER TempTrig INSTEAD OF INSERT ON Song1 FOR EACH ROW BEGIN
INSERT OR IGNORE INTO Album (title) SELECT NEW.album;
UPDATE TempRowIDs SET album_id = (SELECT COALESCE (
(SELECT rowid FROM Album WHERE changes()==0 AND title==NEW.album), last_insert_rowid()
) ) WHERE rowid==1;
INSERT OR IGNORE INTO Artist (name) SELECT NEW.artist;
UPDATE TempRowIDs SET artist_id = (SELECT COALESCE (
(SELECT rowid FROM Artist WHERE changes()==0 AND name==NEW.artist), last_insert_rowid()
) ) WHERE rowid==1;
INSERT INTO Song2 (title, album_rowid, artist_rowid) SELECT
NEW.title, (SELECT album_id FROM TempRowIDs), (SELECT artist_id FROM TempRowIDs);
END;
INSERT INTO TempRowIDs DEFAULT VALUES;
INSERT INTO Song1 (title, album, artist) SELECT title, album, artist FROM Song;
DROP TRIGGER TempTrig;
DROP TABLE TempRowIDs;
-- Conversion ends here
-- Print results
SELECT * FROM Song;
SELECT * FROM Song1;
-- Check if original and copy are identical (https://stackoverflow.com/a/13865679/43615)
SELECT CASE WHEN (SELECT COUNT(*) FROM (SELECT * FROM Song UNION SELECT * FROM Song1)) == (SELECT COUNT() FROM Song) THEN 'Success' ELSE 'Failure' END;
Note that this example has one potential issue: If the constraints on the foreign table are more complex, the SELECT rowid FROM search for the existing entry needs to be updated accordingly. Ideally, SQLite should provide a way to determine the conflicting rowid somehow, but it doesn't, unfortunately (see this related question).
Simple solution without triggers:
create VEHICLES_TEMP table including the CAR_ID
create your new CARS table without the VEHICLES columns you don't want
update CARS with VEHICLE_ID taken from VEHICLES_TEMP (identified by the CAR_ID)
create final VEHICLES table without the CAR_ID
Create a table New_Cars and a INSTEAD OF INSERT trigger, which will insert data to both tables Vehicles and Cars. When inserting to Cars, you can use last_insert_rowid() function to refer to inserted row in Vehicles table.
This can be temporary solution, or you can leave it in your database for further modifications.

SQLite list ALL foreign keys in a database

Is there a way of listing ALL foreign keys in a SQLite database?
They don't seem to be stored in sqlite_master and PRAGMA foreign_key_list('table') only lists one at a time.
Alternatively, is there a way of listing what foreign keys reference a table?
It seems that all (or many) of the PRAGMA commands can be programatically selected with a little trick;
Usually the are called like:
PRAGMA table_info('my_table');
PRAGMA foreign_key_list('my_table');
But this can also be done:
SELECT * FROM pragma_table_info('my_table');
SELECT * FROM pragma_foreign_key_list('my_table');
And the schema can also be (more or less) obtained:
.schema pragma_table_info
/* pragma_table_info(cid,name,type,"notnull",dflt_value,pk) */;
.schema pragma_foreign_key_list
/* pragma_foreign_key_list(id,seq,"table","from","to",on_update,on_delete,"match") */
So, to get all the fks a JOIN between sqlite_master and pragma_foreign_key_list can do the trick:
SELECT
m.name
, p.*
FROM
sqlite_master m
JOIN pragma_foreign_key_list(m.name) p ON m.name != p."table"
WHERE m.type = 'table'
ORDER BY m.name
;
Just take care, that some fields of pragma_foreign_key_list like table, from, ... must be quoted;
With the SQLite shell, use the .schema instruction, and use GREP to filter lines containing REFERENCES.
From shell.c in the SQLite repository, today's version in the trunk, two queries:
SELECT sql
FROM (
SELECT sql sql, type type, tbl_name tbl_name, name name
FROM sqlite_master
UNION ALL
SELECT sql, type, tbl_name, name
FROM sqlite_temp_master
)
WHERE tbl_name LIKE shellstatic()
AND type != 'meta'
AND sql NOTNULL
ORDER BY substr(type, 2, 1), name
and
SELECT sql
FROM (
SELECT sql sql, type type, tbl_name tbl_name, name name
FROM sqlite_master
UNION ALL
SELECT sql, type, tbl_name, name
FROM sqlite_temp_master
)
WHERE type != 'meta'
AND sql NOTNULL
AND name NOT LIKE 'sqlite_%'
ORDER BY substr(type, 2, 1), name
The second one is probably what you are looking for.

Resources