google cloud spanner parents, children, grandparents, grandchildren - google-cloud-platform

some background:
I am building a schema where one account creates a session which is basically a payment and when some other account decides to pay a transaction is generated and the money is transferred to the account that initiated the session.
I want to be able to perform all kinds of queries on these table and I want this to be as efficient as possible.
I came up with this schema:
CREATE TABLE account (
accountId STRING(MAX) NOT NULL,
balance FLOAT64 NOT NULL,
) PRIMARY KEY(accountId);
CREATE TABLE session (
accountId STRING(MAX) NOT NULL,
sessionId STRING(MAX) NOT NULL,
amount FLOAT64 NOT NULL,
) PRIMARY KEY(accountId, sessionId),
INTERLEAVE IN PARENT account ON DELETE CASCADE;
CREATE TABLE `transaction` (
transactionId STRING(MAX) NOT NULL,
sessionId STRING(MAX) NOT NULL,
accountId STRING(MAX) NOT NULL,
) PRIMARY KEY(sessionId, transactionId),
INTERLEAVE IN PARENT session ON DELETE CASCADE;
CREATE INDEX PayersAccountForSession ON `transaction`(accountId),
INTERLEAVE IN account
Suppose we have two accounts with id account1 and account2. account1 creates a session for 100€ with id session so we get a row (account1, session, 100) in the session table. and then account2 decides to pay the 100€ and a transaction with id transaction is created giving us a row (transaction, session, account2) in the transaction table.
Right now this doesn't work because in the transaction table I have no reference to the accountId key from the parent table but in my case this accountId is a different id then the one in the session table.
I could do this:
CREATE TABLE `transaction` (
transactionId STRING(MAX) NOT NULL,
sessionId STRING(MAX) NOT NULL,
accountId STRING(MAX) NOT NULL,
payersAccountId STRING(MAX) NOT NULL,
) PRIMARY KEY(sessionId, accountId, transactionId),
INTERLEAVE IN PARENT session ON DELETE CASCADE;
But now I haven't found any way to create an interleaved index for the payersAccountId to the account table because the name doesn't match any key column in that table. This seems kind of weird to me I think it should be possible to create interleaved indexes to columns with different names (maybe it is but I am missing something).
What I need is a way to resolve this in a way that keeps all account, sessions and transactions in the same split and to be able to query accounts of the payer and the receiver starting in the transaction table. Also I would like to know if there is any way to create multiple interleaved indexes in the same table but based on different columns like in the example for accountId and payerAccountId.
Thanks for taking your time :)

It's not possible to interleave tables if they don't match with the column name.
Adding the payersAccountId to the transaction table is a good idea, with this you could do a query matching the payersAccountId with the sessionId and the accountid like the following:
SELECT * from account WHERE accountId = (SELECT payersAccountId FROM transaction WHERE sessionId = 1)

Related

delete the record based on ID in one table if all the records of same Id's deleted in another table

pls help me with logic.
I have two tables of customers and transactions and there is column action I, U, D. If column action is I or U upsert the data if it is D delete the data in transactions tables.If all records of same transaction id are deleted then delete customers record else delete the transactions record
We can do insert,upsert,delete using Update strategy in transaction table but how can we delete the customer record if the same transaction IDs deleted
You need to create a logic ( like you said ) to delete from customer table. And its safer to either create a new pipeline in same mapping or a brand new mapping.
So, you will read customer_key from customer, do a lookup into transaction table(condition on customer_key), if you see no row found, delete that customer.
Read all customer_key from customer table.
Lookup on transaction table on customer_key. return customer_key.
Use update strategy, link customer_key from SQ #1 and customer_key from lookup. create a condition like this
IIF ( lkp_customer_key is null, DD_DELETE)
Link customer_key from SQ #1 to the customer target.
You can do this using left join too in source qualifier as well.
most of database servers on delete, it cascade the update on respective tables

Is Google Cloud Spanner table have any TTL(Time to Live) setup for required data or table

Dear Cloud Spanner Support
Is there any option for TTL(Time to Live) on Google Cloud Spanner table .
If yes Please provide any additional information
Thank you
Sreeni
Sorry, it's not supported at the moment. It is recommended to submit this as a feature request to the engineering team in the Google Public Issue Tracker.
As of 21-July-2021, Cloud Spanner supports table TTL (in public preview) via the ROW DELETION POLICY clause of CREATE TABLE or ADD DELETION POLICY clause of ALTER TABLE
See the Cloud Documentation for additional information.
CREATE TABLE MyTable(
Key INT64,
CreatedAt TIMESTAMP,
) PRIMARY KEY (Key)
, ROW DELETION POLICY (OLDER_THAN(CreatedAt, INTERVAL 30 DAY));
Let suppose we have following scenario:
You have table with say load_date and data should be deleted when load_date < now - 30
You can create parent table with only one column load_date (and maybe some additional stats per this day if needed) - after this you create interleaved child table with all information and with cascade deletion
So when you need to clean up child table you just delete one record from parent and all associated data will be removed from child automatically
CREATE TABLE parent (
parent_id STRING(MAX) NOT NULL,
load_time TIMESTAMP
) PRIMARY KEY(parent_id);
CREATE TABLE child (
parent_id STRING(MAX) NOT NULL,
child_id INT64 NOT NULL,
data_1 STRING(MAX) NOT NULL,
data_2 INT64 NOT NULL
) PRIMARY KEY(parent_id, child_id),
INTERLEAVE IN PARENT parent ON DELETE CASCADE;

Understanding Secondary Indexes

if i have Table
Table
CREATE TABLE Users (
userId STRING(36) NOT NULL,
contactName STRING(300) NOT NULL,
eMail STRING(100) NOT NULL,
....
) PRIMARY KEY (userId)
and secondary index
CREATE NULL_FILTERED INDEX ActiveUsersByEMail
ON Users (
eMail,
isActive,
)
and i select record by:
SELECT * FROM Users WHERE eMail = 'test#test.com' AND isActive = TRUE
spanner will automatically look at index, take userId and give me a record ?.
or i need to create
CREATE NULL_FILTERED INDEX ActiveUsersByEMail_01
ON Users (
eMail,
isActive,
userId
)
and first take userId by:
SELECT userId from Users#{FORCE_INDEX=ActiveUsersByEMail_01} WHERE eMail = 'test#test.com' AND isActive = TRUE
and then i take a record by:
`SELECT * FROM Users WHERE userId = '${userId}'``
Question is automatically use or not spanner secondary indices for standard select if condition match secondary index keys?
You should use FORCE_INDEX as Cloud Spanner will only choose an index in rare circumstances as stated here. You can use the STORING clause to add data directly to the index, allowing you to read the data directly from the index to avoid the second call. This is suggested for common query patterns in your application.
In github i ask same question and It turned out that this is easily done (without creating additional index) by:
SELECT * from Users#{FORCE_INDEX=ActiveUsersByEMail} WHERE eMail = 'test#test.com' AND isActive = TRUE
At this time the search is going on index and row come with all fields

Having trouble summing columns in SQL Server joined view

Moi guys, Matt here. I'm having trouble with a relatively complicated view. I have a parts and service table that each have unique identifiers for a given part/service. I'm trying to link these to a service invoice table and subsequent view as a M:N relationship, so I've set up intermediary relational tables, with both the invoice number (invoice primary key) and part/service number (part/service primary key) as the combined primary key. Here's my code for the whole relationship and view:
CREATE TABLE service_invoice
( servinv_Num VARCHAR2(10) CONSTRAINT serv_snum_PK PRIMARY KEY,
servinv_EmpID NUMBER(6) CONSTRAINT serv_empnum_FK REFERENCES employee(empID),
servinv_CustID NUMBER(6) CONSTRAINT serv_custid_FK REFERENCES customer(custID),
servinv_VIN VARCHAR2(25) CONSTRAINT serv_VIN_FK REFERENCES vehicle(vehicle_vin),
servinv_Terms VARCHAR2(6) CONSTRAINT serv_trms_NN NOT NULL,
servinv_Date DATE );
CREATE TABLE Parts
( PartID VARCHAR2(10) CONSTRAINT Part_PartID_PK PRIMARY KEY,
PartDesc VARCHAR2(50) CONSTRAINT Part_PartDesc_NN NOT NULL,
PartCharge NUMBER(4,2) CONSTRAINT Part_PartCharge_NN NOT NULL );
CREATE TABLE Service
( ServiceID VARCHAR2(10) CONSTRAINT Serv_ServID_PK PRIMARY KEY,
ServDesc VARCHAR2(50) CONSTRAINT Serv_ServName_NN NOT NULL,
ServCharge NUMBER(4,2) CONSTRAINT Serv_ServCharge_NN NOT NULL );
CREATE TABLE Serv_SI_Rel
( SI_num VARCHAR2(10) CONSTRAINT ServSI_SInum_FK REFERENCES service_invoice(servinv_Num),
ServiceID VARCHAR2(10) CONSTRAINT ServSI_ServID_FK REFERENCES Service(ServiceID),
CONSTRAINT ServSI_SInum_ServID_PK PRIMARY KEY(SI_num, ServiceID) );
CREATE TABLE Parts_SI_Rel
( SI_num VARCHAR2(10) CONSTRAINT PartSI_SInum_FK REFERENCES service_invoice(servinv_Num),
PartID VARCHAR2(10) CONSTRAINT PartSI_PartID_FK REFERENCES Parts(PartID),
CONSTRAINT PartSI_SInum_PartID_PK PRIMARY KEY(SI_num, PartID) );
CREATE OR REPLACE VIEW ServiceInvoiceDoc
AS
(
SELECT si.servinv_Num, si.servinv_Date, si.servinv_Terms,
es.empName,
sc.custName, sc.custHouse, sc.custCity,
sc.custState, sc.custZIP, sc.custPhone, sc.custEmail,
sv.vehicle_VIN, sv.vehicle_mileage,
srel.ServiceID,
prel.PartID,
s.ServDesc, s.ServCharge,
p.PartDesc, p.PartCharge,
SUM(s.ServCharge) TotalServCharges,
SUM(p.PartCharge) TotalPartsCharges,
( SUM(s.ServCharge)+SUM(p.PartCharge) ) SubTotalCharges,
( SUM(s.ServCharge)+SUM(p.PartCharge) )*0.0825 Taxes,
( SUM(s.ServCharge)+SUM(p.PartCharge) )*1.0825 TotalCharges
FROM service_invoice si
JOIN employee es
ON (es.empID = si.servinv_EmpID)
JOIN customer sc
ON (sc.custID = si.servinv_CustID)
JOIN vehicle sv
ON (sv.vehicle_VIN = si.servinv_VIN)
LEFT OUTER JOIN Serv_SI_Rel srel
ON (srel.SI_Num = si.servinv_Num)
LEFT OUTER JOIN Parts_SI_Rel prel
ON (prel.SI_num = si.servinv_Num)
JOIN Parts p
ON (prel.PartID = p.PartID)
JOIN Service s
ON (srel.ServiceID = s.ServiceID) );
The error I get has to do with summing the individual parts and service charges in the M:N relationship. Here's the error code from the run:
ORA-00937: not a single-group group function
I've tried fixing with a group by command, but the grouping identifier (service invoice) isn't included on the part or service tables, and the joins don't seem to link these up for a group. e.g. I tried calling GROUP BY si.servinv_Num
Can this be resolved at all or is it completely wrong? I have the option of dropping the M:N relationship as a 1:M and simply making a separate invoice for each part/service charge, but I would prefer to keep it compact and professional.
Any help would be greatly appreciated. Thank you so much for your time!
a) wrong tag
b) I'd imagine you would need to list all columns in group by clause that aren't aggregated, as per Oracle
...
group by si.servinv_Num, si.servinv_Date, si.servinv_Terms,
es.empName,
sc.custName, sc.custHouse, sc.custCity,
sc.custState, sc.custZIP, sc.custPhone, sc.custEmail,
sv.vehicle_VIN, sv.vehicle_mileage,
srel.ServiceID,
prel.PartID,
s.ServDesc, s.ServCharge,
p.PartDesc, p.PartCharge

Complex SQL syntax

I have a game, and in the database I'm saving the user actions by date & time.
CREATE TABLE user_actions
(
aId BIGSERIAL PRIMARY KEY NOT NULL,
userId BIGINT NOT NULL REFERENCES users(uId) DEFERRABLE INITIALLY DEFERRED,
aDate TIMESTAMP without time zone DEFAULT now(),
aType INTEGER NOT NULL DEFAULT 0
);
My users are identified with email
CREATE TABLE users(
uId BIGSERIAL PRIMARY KEY NOT NULL,
uName VARCHAR (50) NOT NULL,
uEmail VARCHAR (75) UNIQUE NULL
);
and each day new prizes are added each day has a different number of prizes
CREATE TABLE prizes(
pId BIGSERIAL PRIMARY KEY NOT NULL,
pDate TIMESTAMP without time zone DEFAULT now(),
pType INTEGER NULL
pSize INTEGER NULL
);
This query list the userId and his last action date, per user
select distinct userId, max(aDate) from user_actions GROUP BY userId order by userId;
I want to create a query that will count the number of prizes added since each user last action.
I'm running:
OS: Debian
DB: Postgresql
code: Django
I think I will use CTE though It has not been tested
WITH last_actions AS (
SELECT DISTINCT userId, MAX(aDate) as last_logged
FROM user_actions
GROUP BY userId ORDER BY userId)
SELECT a.userId, COUNT(b.pDate)
FROM last_actions a, prizes b
WHERE b.pDate >= a.last_logged
GROUP BY a.userId;