Can a Cloud Spanner child table have the same primary key as the parent table? - parent-child

Can a Cloud Spanner child table define the same primary key that the parent table uses (with the child is interleaved in the parent)? I know the child's primary key must use the parent's primary key as a prefix, but is the child key required to use at least one additional column? If it's permitted to use the same key, is it bad practice? And if it's not permitted, why not?
For example:
CREATE TABLE Furniture (
FurnitureId STRING(MAX) NOT NULL,
MakerId INT64 NOT NULL,
// additional fields here...
) PRIMARY KEY (FurnitureId, MakerId);
CREATE TABLE FurnitureHistory (
FurnitureId STRING(MAX) NOT NULL,
MakerId INT64 NOT NULL,
// additional fields here...
) PRIMARY KEY (FurnitureId, MakerId),
INTERLEAVE IN PARENT Furniture;
Thanks!
Peter

It is permitted, but it seems a little bit strange at first hand. This data model will allow you to insert at most one child row for each parent row. This is quite similar to adding a couple of optional fields to the parent table, but with these optional fields logically separated from the parent table. So I guess this makes sense if:
You have a number of additional fields that have a logical different meaning, which gives you a reason for storing them in a child table.
And/or
The additional fields should either not be filled at all, or all (or at least more than one) should be filled when one of them are filled.

Related

Seeking improvements for this database design

I have a database design that works, but is probably very inefficient. I'd welcome suggestions about how to make it more efficient, whether it's redesigning the tables or views, or adding indexes. My SQL skills are mediocre, so I'm seeking guidance.
Back story: I'm developing a database that represents objects and their relationships with each other. It has two tables. "objects" holds data about objects, such as hashes, strings, numbers, etc. "relationships" establishes the relationships between hashes and other objects.
The "fclass" field requires some explanation. fclass represents the class of the object. In this example, the only two values are 'h' for hash and 's' for scalar (a string, number, boolean, or null). fclass might have many other values that represent derivatives of hash, such as 'person' or 'product'.
One requirement is the ability to query for hashes that have a certain fclass and contain certain key/value pairs. My current strategy is to create a view that lists each key/value pair along with its parent element and the parent's fclass. Here's the design for the database.
-- objects
create table objects (
pk varchar(36) primary key,
fclass text not null, -- type of object: h=hash, s=scalar, but might be others
scalar text -- the value of a scalar object
);
-- insert a hash and a scalar
insert into objects(pk, fclass) values('a', 'h');
insert into objects(pk, fclass, scalar) values('b', 's', 'fred#example.com');
-- relationships
create table relationships (
pk varchar(36) primary key,
parent varchar(36) not null references objects(pk) on delete cascade,
child varchar(36) not null references objects(pk) on delete cascade,
hkey varchar(255)
);
-- set a scalar as an element for a hash
insert into relationships(pk, parent, child, hkey) values('c', 'a', 'b', 'email');
-- view of key/value pairs
create view hash_pairs as
select r.parent, r.child, r.hkey, c.scalar
from objects c, relationships r
where (c.pk = r.child) and (r.hkey is not null);
-- view of parent elements with the parent fclasses
create view hash_values as
select hp.parent, p.fclass, hp.child, hp.hkey, hp.scalar
from hash_pairs hp, objects p
where (hp.parent = p.pk);
select * from hash_values;
Selecting from hash_values produces output like this:
parent fclass child hkey scalar
---------- ---------- ---------- ---------- ----------------
a h b email fred#example.com
That's the output I want. I just want to get to it more efficiently. Any suggestions on how to improve performance?

Literal (raw) value as foreign key in sqlite

is it possible to make something like this in sqlite ?
FOREIGN KEY(TypeCode, 'ARawValue', IdServeur) REFERENCES OTHERTABLE(TypeCode, TypeElem, IdServeur)
it says unknown column "ARawValue" in foreign key definition, is't there another way ?
No, this is not possible. A foreign key constraint must be defined only by columns in the child and parent tables.
Probably the next best solution is to add a column to the child table with a default value set to the literal value (and optionally a check constraint that restricts the column to that single value).
Strictly speaking, an sqlite unique partial index should have been a good alternative solution, but it did not work for me in testing on version 3.28.0. A partial index is an index defined with a WHERE clause and can even be marked as UNIQUE. The official foreign key documentation requires a UNIQUE index on the parent table. There is no explicit exclusion of partial indexes, so I thought it would be a good solution. I was able to create the index and even define the foreign key constraint on the partial index, but no matter what I tried I got a foreign key error upon INSERT into the child table, even when I had verified that the parent table contained a unique pair of values as defined by the index.

Inserting a row at a specific position?

So I created a recipe database, and just now I've noticed I forgot to add an ingredient to the second recipe. The order of ingredients is obviously important here, so even if I add it now to the end of the table, it will still be the last when I SELECT the second recipe, when it should be the second ingredient.
Is there any way I can insert it in a specific position, or am I doomed and will have to create an index column specifying the order of the ingredients?
NOTE: This is a junction table, so there's no primary key here, thus I can't insert it using a specific primary key value.
EDIT: Basically I have three tables: Recipe, Ingredient, and RecipeIngredient many-to-many junction table.
Here's the RecipeIngredient junction table structure:
RecipeId: FK
IngredientId: FK
Quantity: REAL
UOM: TEXT
Notes: TEXT
The rules of the First normal form (1NF) are strict on this:
There's no top-to-bottom ordering to the rows.
Meaning there is no way, in a proper database schema, that a record can be "missing at a certain position".
You are indeed "doomed" and
will have to create an index column specifying the order of the ingredients
can you show the table structure?
It is difficult to insert a row in specific position without primary key.

DynamoDB - Global Secondary Index on set items

I have a dynamo table with the following attributes :
id (Number - primary key )
title (String)
created_at (Number - long)
tags (StringSet - contains a set of tags say android, ios, etc.,)
I want to be able to query by tags - get me all the items tagged android. How can I do that in DynamoDB? It appears that global secondary index can be built only on ScalarDataTypes (which is Number and String) and not on items inside a set.
If the approach I am taking is wrong, an alternative way for doing it either by creating different tables or changing the attributes is also fine.
DynamoDB is not designed to optimize indexing on set values. Below is a copy of the amazon's relevant documentation (from Improving Data Access with Secondary Indexes in DynamoDB).
The key schema for the index. Every attribute in the index key schema
must be a top-level attribute of type String, Number, or Binary.
Nested attributes and multi-valued sets are not allowed. Other
requirements for the key schema depend on the type of index: For a
global secondary index, the hash attribute can be any scalar table
attribute. A range attribute is optional, and it too can be any scalar
table attribute. For a local secondary index, the hash attribute must
be the same as the table's hash attribute, and the range attribute
must be a non-key table attribute.
Amazon recommends creating a separate one-to-many table for these kind of problems. More info here : Use one to many tables
This is a really old post, sorry to revive it, but I'd take a look at "Single Table Design"
Basically, stop thinking about your data as structured data - embrace denormalization
id (Number - primary key )
title (String)
created_at (Number - long)
tags (StringSet - contains a set of tags say android, ios, etc.,)
Instead of a nosql table with a "header" of this:
id|title|created_at|tags
think of it like this:
pk|sk |data....
id|id |{title, created_at}
id|id+tag|{id, tag} <- create one record per tag
You can still return everything by querying for pk=id & sk begins with id and join the tags to the id records in your app logic
and you can use a GSI to project id|id+tag into tag|id which will still require you to write two queries against your data to get items of a given tag (get the ids then get the items), but you won't have to duplicate your data, you wont have to scan and you'll still be able to get your items in one query when your access pattern doesn't rely on tags.
FWIW I'd start by thinking about all of your access patterns, and from there think about how you can structure composite keys and/or GSIs
cheers
You will need to create a separate table for this query.
If you are interested in fetching all items based on a tag then I suggest keeping a table with a primary key:
hash: tag
range: id
This way you can use a very simple Query to fetch all items by tag.

Cascading List of Values with many to many relationship

I am developing an application which tracks class attendance of students in a school, in Apex.
I want to create a page with three level cascading select lists, so the teacher can first select the Semester, then the Subject and then the specific Class of that Subject, so the application returns the Students who are enrolled in that Class.
My problem is that these three tables have a many-to-many relationship between them, so I use extra tables with their keys.
Every Semester has many Subjects and a Subject can be taught in many Semesters.
Every Subject has many classes in every Semester.
The students must enroll in a subject every semester and then the teacher can assign them to a class.
The tables look something like this:
create table semester(
id number not null,
name varchar2(20) not null,
primary key(id)
);
create table subject(
id number not null,
subject_name varchar2(50) not null,
primary key(id)
);
create table student(
id number not null,
name varchar2(20),
primary key(id)
);
create table semester_subject(
id number not null,
semester_id number not null,
subject_id number not null,
primary key(id),
foreign key(semester_id) references semester(id),
foreign key(subject_id) references subject(id),
constraint unique sem_sub_uq unique(semester_id, subject_id)
);
create table class(
id number not null,
name number not null,
semester_subject_id number not null,
primary key(id),
foreign key(semester_subject_id) references semester_subject(id)
);
create table class_enrollment(
id number not null,
student_id number not null,
semester_subject_id number not null,
class_id number,
primary_key(id),
foreign key(student_id) references student(id),
foreign key(semester_subject_id) references semester_subject(id),
foreign key(class_id) references class(id)
);
The list of value for the Semester select list looks like this:
select name, id
from semester
order by 1;
The the subject select list should include the names of all the Subjects available in the semester selected above, but I can't figure the query or even if it's possible. What I have right now:
select s.name, s.id
from subject s, semester_subject ss
where ss.semester_id = :PX_SEMESTER //value from above select list
and ss.subject_id = s.id;
But you can't have two tables in a LoV and the query is probably wrong anyway...
I didn't even begin to think about what the query for the class would look like.
I appreciate any help or if you can point me in the right direction so I can figure it out myself.
Developing an Apex Input Form Using Item-Parametrized Lists of Values (LOVs)
Your initial schema design looks good. One recommendation once you've developed and tested your solution on a smaller scale, append to the ID (primary key) columns a trigger that can auto-populate its values through a sequence. You could also skip the trigger and just reference the sequence in your sql insert DML commands. It just makes things simpler. Creating tables in the APEX environment with their built-in wizards offer the opportunity to make an "auto-incrementing" key column.
There is also an additional column added to the SEMESTER table called SORT_KEY. This helps when you are storing string typed values which have logical sorting sequences that aren't exactly alphanumeric in nature.
Setting Up The Test Data Values
Here is the test data I generated to demonstrate the cascading list of values design that will work with the example.
Making Dynamic List of Value Queries
The next step is to make the first three inter-dependent List of Values definitions. As you have discovered, you can reference page parameters in your LOVs which may come from a variety of sources. In this case, the choice selection from our LOVs will be assigned to Apex Page Items.
I also thought only one table could be referenced in a single LOV query. This is incorrect. The page documentation suggests that it is the SQL query syntax that is the limiting factor. The following LOV queries reference more than one table, and they work:
-- SEMESTER LOV Query
-- name: CHOOSE_SEMESTER
select a.name d, a.id r
from semester a
where a.id in (
select b.semester_id
from semester_subject b
where b.subject_id = nvl(:P5_SUBJECT, b.subject_id))
order by a.sort_id
-- SUBJECT LOV Query
-- name: CHOOSE_SUBJECT
select a.subject_name d, a.id r
from subject a
where a.id in (
select b.subject_id
from semester_subject b
where b.semester_id = nvl(:P5_SEMESTER, b.semester_id))
order by 1
-- CLASS LOV Query
-- name: CHOOSE_CLASS
select a.name d, a.id r
from class a, semester_subject b
where a.semester_subject_id = b.id
and b.subject_id = :P5_SUBJECT
and b.semester_id = :P5_SEMESTER
order by 1
Some design notes to consider:
Don't mind the P5_ITEM notation. The page in my sample app happened to be on "page 5" and so the convention goes.
I chose to assign a name for each LOV query as a hint. Don't just embed the query in an item. Add some breathing room for yourself as a developer by making the LOV a portable object that can be referenced elsewhere if needed.
MAKE a named LOV for each query through the SHARED OBJECTS menu option of your application designer.
The extra operator involving the NVL command, as in nvl(:P5_SUBJECT, b.subject_id) for the CHOOSE_SEMESTER LOV is an expression mirrored on the CHOOSE_SUBJECT query as well. If the default value of P5_SUBJECT and P5_SEMESTER are null when entering the page, how does that assist with the handling of the cascading relationships?
The table SEMESTER_SUBJECT represents a key relationship. Why is a LOV for this table not needed?
APEX Application Form Design Using Cascading LOVs
Setting up the a page for testing the schema design and LOV queries requires the creation of three page items:
Each page item should be defined as a SELECT LIST leave all the defaults initially until you understand how the basic design works. Each select list item should be associated with their corresponding LOV, such as:
The key design twist is the Select List made for the CHOOSE_CLASS LOV, which represents a cascading dependency on more than one data source.
We will use the "Cascading Parent" option so that this item will wait until both CHOOSE_SEMESTER and CHOOSE_SUBJECT are selected. It will also refresh if either of the two are changed.
YES! The cascading parent item can consist of multiple page items/elements. They just have to be declared in a comma separated list.
From the online help info, this is a general introduction to how cascading LOVs can be used in APEX designs:
From Oracle Apex Help Docs: A cascading LOV means that the current item's list of values should be refreshed if the value of another item on this page gets changed.
Specify a comma separated list of page items to be used to trigger the refresh. You can then use those page items in the where clause of your "List of Values" SQL statement.
Demonstration of APEX Application Items with Cascading LOVs
These examples are based on the sample data given at the beginning of this solution. The path of the chosen example case is:
SEMESTER: SPRING 2014 + SUBJECT: PHYS ED + Verify Valid Course Options:
Fitness for Life
General Flexibility
Presidential Fitness Challenge
Running for Fun
Volleyball Basics
The choice from above will be assigned to page item P5_CLASS.
Selection Choices for P5_SEMESTER:
Selection Choices for P5_SUBJECT:
Selection Choices for P5_CLASS:
Closing Remarks and Discussion
Some closing thoughts that occurred to me while working with this design project:
About the Primary Keys: The notion of a generic, ID named column for a primary key was a good design choice. While APEX can handle composite business keys, it gets clumsy and difficult to work around.
One thing that made the schema design challenging to work with was that the notion of "id" transformed in the other tables that referenced it. (Such as the ID column in the SEMESTER table became SEMESTER_ID in the SEMESTER_SUBJECT table. Just keep an eye on these name changes with larger queries. At times I actually lost track exactly what ID I was working with.
A Word for Sanity: In the likely event you decide to assign ID values through a database sequence object, the default is usually to begin at one. If you have several different tables in your schema with the same column name: ID and some associating tables such as CLASS_ENROLLMENT which connects the values of one primary key ID and three additional foreign key ID's, it may get difficult to discern where the data values are coming from.
Consider offsetting your sequences or arbitrarily choosing different increments and starting values. If you're mainly pushing ID's around in your queries, if two different ID sets are separated by two or three orders of magnitude, it will be easy to know if you've pulled the right data values.
Are There MORE Cascading Relationships? If a "parent" item relationship indicates a dependency that makes a page item LOV wait or change depending on the value of another, could there be another cascading relationship to define? In the case of CHOOSE_SEMESTER and CHOOSE_SUBJECT is it possible? Is it necessary?
I was able to figure out how to make these two items hold an optional cascading dependency, but it required setting up another outside page item reference. (If it isn't optional, you get stuck in a closed loop as soon as one of the two values changes.) Fancy, but not really necessary to solve the problem at hand.
What's Left to Do? I left out some additional tasks for you to continue with, such as managing the DML into the ENROLLMENT table after selecting a valid STUDENT.
Overall, you've got a workable schema design. There is a way to represent the data relationships through an APEX application design pattern. Happy coding, it looks like a challenging project!

Resources