Interactive Grid:Process with PL/SQL only that isn't based off a table - plsql

Env: Oracle APEX v5.1 with Oracle 12c Release 2
Firstly, I have created an Interactive Grid that isn't based off an underlying table as I will process this manually using PL/SQL.
I have been using the following as a guide:
https://apex.oracle.com/pls/apex/germancommunities/apexcommunity/tipp/6361/index-en.html
I basically have the following query:
select
level as id,
level as grid_row,
null as product,
null as product_item
from dual connect by level <= 1
Concentrating on just the product and product_item columns where the product_item column will be a readonly column and only the product number can be entered, I would like to achieve the following:
Product Product Item
---------- -------------
123456 123456-1
123456 123456-2
556677 556677-1
654321 654321-1
654321 654321-2
654321 654321-3
123456 123456-3
From the above, as the user types in the Product and then tabs out of the field, I would like a DA to fire that will add the sequence of "-1" to the end of that product number. Then is the user then adds another row within the IG and enters the same product number, I then want it to append "-2" to the end of it.
Only when the product changes number, I need the sequence to reset to "-1" for that new product as per 556677 and so forth.
Other scenarios that should also be taken into consideration are as follows:
From above IG, the user entered 123456 again but this should calculate that the next sequence for 123456 is "-3"
The same needs to be catered for, when a Product is removed from the IG but to always look at the max sequence number for that product.
I was thinking of possibly using APEX_COLLECTIONS as a means of storing what is currently in the grid, since no changes have been committed to the database.

Assuming you have a collection of product values (in this case, I am using the built-in SYS.ODCINUMBERLIST which is a VARRAY data type) then the SQL for your output would be:
SELECT id,
id AS grid_row,
product,
product || '-' || ROW_NUMBER() OVER ( PARTITION BY product ORDER BY id )
AS product_item
FROM (
SELECT ROWNUM AS id,
COLUMN_VALUE AS product
FROM TABLE(
SYS.ODCINUMBERLIST(
123456,
123456,
556677,
654321,
654321,
654321,
123456
)
)
)
ORDER BY id
Output:
ID | GRID_ROW | PRODUCT | PRODUCT_ITEM
-: | -------: | ------: | :-----------
1 | 1 | 123456 | 123456-1
2 | 2 | 123456 | 123456-2
3 | 3 | 556677 | 556677-1
4 | 4 | 654321 | 654321-1
5 | 5 | 654321 | 654321-2
6 | 6 | 654321 | 654321-3
7 | 7 | 123456 | 123456-3
db<>fiddle here

As you mentioned, the data you enter is not saved into the DB whilst you are inserting your products, so it is not in fact stored anywhere.
So you cannot go check if that value already exists and enter a -2 or other.
Some things to consider would be to maybe save the values into a temp table so you can then have a function go check how many product_item like 123456-% are in there and use that number +1 as your new product_item.
Or you could go the even harder way and do it all with javascript. For this you will need to somehow get all records in the IG, go through them all and see how many occurences of 123456 you have and then insert 123456-(no of occurences + 1).

Related

Enforce uniqueness within a date range or based on the value of another column

I have a table with a large amount of data; moving forward, I would like to enforce uniqueness for a given column in this table. However, the table contains a large amount of rows where that column is non-unique. I am not able to delete or alter these rows.
Is it possible to enforce uniqueness over a given date range, or since a specific date, or based on the value of another column (or something else like that) in MariaDB?
You can create a UNIQUE index on multiple columns, where one column is nullable. MariaDB will see each column with NULL values as a different value regarding the UNIQUE index, even if the other column values of the UNIQUE index are the same. Check the MariaDB documentation Getting Started with Indexes - Unique Index:
The fact that a UNIQUE constraint can be NULL is often overlooked. In SQL any NULL is never equal to anything, not even to another NULL. Consequently, a UNIQUE constraint will not prevent one from storing duplicate rows if they contain null values:
CREATE TABLE t1 (a INT NOT NULL, b INT, UNIQUE (a,b));
INSERT INTO t1 values (3,NULL), (3, NULL);
SELECT * FROM t1;
+---+------+
| a | b |
+---+------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 3 | NULL |
| 3 | NULL |
+---+------+
You can create such a UNIQUE index on the date column you already have and a new column which indicates if the date value should be unique or not:
CREATE TABLE Foobar(
id INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
createdAt DATE NOT NULL,
dateUniqueMarker BIT NULL DEFAULT 0,
UNIQUE KEY uq_createdAt(createdAt, dateUniqueMarker)
);
INSERT INTO Foobar(createdAt) VALUES ('2021-11-04'),('2021-11-05'),('2021-11-06');
SELECT * FROM Foobar;
+----+------------+------------------------------------+
| id | createdAt | dateUniqueMarker |
+----+------------+------------------------------------+
| 1 | 2021-11-04 | 0x00 |
| 2 | 2021-11-05 | 0x00 |
| 3 | 2021-11-06 | 0x00 |
+----+------------+------------------------------------+
INSERT INTO Foobar(createdAt) VALUES ('2021-11-05');
ERROR 1062 (23000): Duplicate entry '2021-11-05-\x00' for key 'Foobar.uq_createdAt'
UPDATE Foobar SET dateUniqueMarker = NULL WHERE createdAt = '2021-11-05';
INSERT INTO Foobar(createdAt, dateUniqueMarker) VALUES ('2021-11-05', NULL);
SELECT * FROM Foobar;
+----+------------+------------------------------------+
| id | createdAt | dateUniqueMarker |
+----+------------+------------------------------------+
| 1 | 2021-11-04 | 0x00 |
| 2 | 2021-11-05 | NULL |
| 5 | 2021-11-05 | NULL |
| 3 | 2021-11-06 | 0x00 |
+----+------------+------------------------------------+
Without any data example and scenario illustration, it's hard to know. If you can update your question with those information, please do.
"Is it possible to enforce uniqueness over a given date range, or since a specific date, or based on the value of another column (or something else like that) in MariaDB?"
If by "enforce" you mean to create a new column then populate it with unique identifier, then yes it is possible. If what you really mean is to generate a unique value based on other column, that's also possible. Question is, how unique do you want it to be?
Is it like this unique?
column1
column2
column3
unique_val
2021-02-02
ABC
DEF
1
2021-02-02
CBD
FEA
1
2021-02-03
BED
GER
2
2021-02-04
ART
TOY
3
2021-02-04
ZSE
KSL
3
Whereby if it's the same date (on column1), it should have the same unique value regardless of column2 & column3 data.
Or like this?
column1
column2
column3
unique_val
2021-02-02
ABC
DEF
1
2021-02-02
CBD
FEA
2
2021-02-03
BED
GER
3
2021-02-04
ART
TOY
4
2021-02-04
ZSE
KSL
5
Taking all (or certain) columns to consider the unique value.
Both of the scenario above can be achieved in query without the need to alter the table, adding and populate a new column but of course, the latter is also possible.

Multiple Separate WHERE classes in single VIEW

I need help creating a single SELECT statement as part of a CREAT VIEW statement that contains multiple, separate filtering or grouping requirements.
I am working on an SQLite database to track usage of our local food pantry, where we have two types of visitors, “Scheduled” or “Drop-In”, visiting on different days. One of the central tables is the “visit_log” table that tracks each visit by date, time, type of visit, and people in the household.
I’m trying to create a VIEW that summarizes that “visit_log” grouped by the visit_date, and for both number of records and SUM of household size, displaying the number of “Drop-Ins”, the number of “Scheduled” and the total of the two types.
Here is the “visit_log”
CREATE TABLE "visit_log" ("visit_date" DATE, "visit_time" TIME, "client_relation" TEXT, "household_size" INTEGER)
Here is a sample of the “visit_log” table’s content. (We have not started recording the visit_time yet, so those values are blank).
"visit_date","visit_time","client_relation","household_size"
"6/9/20","","Scheduled","1"
"6/9/20","","Scheduled","1"
"6/9/20","","Drop-In","2"
"6/9/20","","Drop-In","3"
"6/9/20","","Drop-In","8"
"6/9/20","","Drop-In","5"
"6/16/2020","","Scheduled","1"
"6/16/2020","","Scheduled","1"
"6/16/2020","","Drop-In","4"
"6/16/2020","","Drop-In","5"
"6/16/2020","","Drop-In","2"
"6/16/2020","","Drop-In","2"
"6/16/2020","","Drop-In","5"
"6/16/2020","","Drop-In","1"
I can create three separate VIEW, one for each type and one for the two combined. But my goal is to have the results of these three VIEWs in one.
Here are the three VIEWs. First is for the two client types combined.
CREATE VIEW "visit_summary" AS SELECT
visit_date,
COUNT (*) AS households_total,
SUM (household_size) AS individuals_total
FROM
"visit_log"
GROUP By visit_date
This yields
"visit_date","households_total","individuals_total"
"06/09/2020","12","44"
"06/16/2020","8","21"
"06/23/2020","7","20"
"06/30/2020","10","22"
"07/07/2020","7","18"
Next is the VIEW for the Drop-Ins
CREATE VIEW "visit_summary_dropin" AS SELECT
visit_date,
COUNT (*) AS households_dropin,
SUM (household_size) AS individuals_dropin
FROM
"visit_log"
WHERE client_relation = "Drop-In"
GROUP By visit_date
This yields
"visit_date","households_dropin","individuals_dropin"
"06/09/2020","10","42"
"06/16/2020","6","19"
"06/23/2020","4","13"
"06/30/2020","6","12"
"07/07/2020","6","16"
Finally is the VIEW for the Scheduled
CREATE VIEW "visit_summary_scheduled" AS SELECT
visit_date,
COUNT (*) AS households_schedualed,
SUM (household_size) AS individuals_scheduled
FROM
"visit_log"
WHERE client_relation = "Scheduled"
GROUP By visit_date
This yields
"visit_date","households_schedualed","individuals_scheduled"
"06/09/2020","2","2"
"06/16/2020","2","2"
"06/23/2020","3","7"
"06/30/2020","4","10"
"07/07/2020","1","2"
What I'm hoping to create is a single VIEW that yields
"visit_date","households_total","individuals_total","households_dropin","individuals_dropin","households_schedualed","individuals_scheduled"
"06/09/2020","12","44","10","42","2","2"
etc…
So my ultimate question, finally, is how to create a single VIEW containing something like multiple WHERE classes to define different columns?
You can do it with conditional aggregation:
CREATE VIEW visit_summary_scheduled_all AS
SELECT visit_date,
COUNT(*) households_total,
SUM(household_size) individuals_total,
SUM(client_relation = 'Drop-In') households_dropin,
SUM(CASE WHEN client_relation = 'Drop-In' THEN household_size END) individuals_dropin,
SUM(client_relation = 'Scheduled') households_scheduled,
SUM(CASE WHEN client_relation = 'Scheduled' THEN household_size END) individuals_scheduled
FROM visit_log
GROUP By visit_date
See the demo.
Results:
| visit_date | households_total | individuals_total | households_dropin | individuals_dropin | households_scheduled | individuals_scheduled |
| ---------- | ---------------- | ----------------- | ----------------- | ------------------ | -------------------- | --------------------- |
| 6/16/2020 | 8 | 21 | 6 | 19 | 2 | 2 |
| 6/9/20 | 6 | 20 | 4 | 18 | 2 | 2 |

How to scan DynamoDB table for retrieving only one item in each partition key

Let say I have a table with partition key "ID" and range key "Time" with the following items:
ID | Time | Data
------------------
A | 1 | abc
A | 2 | def
B | 2 | ghi
B | 3 | jkl
And I want to scan only one item in each partition that has the highest time value in each partition. So the outcome of the scan should look like:
ID | Time | Data
------------------
A | 2 | def
B | 3 | jkl
Is this possible with the DynamoDB's scan feature?
(I want to avoid scan all and do such filtering by myself).
If you want to fetch just a few IDs along with their highest Time, you can query with reverse index, so for every ID, you will have only 1 item read. But for this you need an existing list of IDs.
So for each ID, there will be:
1 query
1 item read
Otherwise, the only way is to scan everything unfortunately.

How to rank rows in a table in sqlite?

How can I create a column that has ranked the information of the table based on two or three keys?
For example, in this table the rank variable is based on Department and Name:
Dep | Name | Rank
----+------+------
1 | Jeff | 1
1 | Jeff | 2
1 | Paul | 1
2 | Nick | 1
2 | Nick | 2
I have found this solution but it's in SQL and I don't think it applies to my case as all information is in one table and the responses seem to SELECT and JOIN combine information from different tables.
Thank you in advance
You can count how many rows come before the current row in the current group:
UPDATE MyTable
SET Rank = (SELECT COUNT(*)
FROM MyTable AS T2
WHERE T2.Dep = MyTable.Dep
AND T2.Name = MyTable.Name
AND T2.rowid <= MyTable.rowid);
(The rowid column is used to differentiate between otherwise identical rows. Use the primary key, if you have one.)

Logic behind loading dimension tables

How do I populate Dim_tbls from a relational source?
These example tables are given:
tbl_sales: id_sales, fk_id_customer, fk_id_product, country, timestamp
tbl_customer: id_customer, name, adress, zip, city
tbl_product: id_product, price, product
My goal is to get these attributes into a start-schema. The problem I have is the logic behind loading the dimension tables. I mean, what data would I load into the Dim_Product? All the products that are in tbl_product? But how would I know how many Sales are done with a specific product?
Analysis I would like to do are:
How many people bought product x.
How many sales are made from city x.
How many sales were made between Time x and y.
Example data:
tbl_sales: id_sales | fk_id_customer | fk_id_product | country | timestamp
1 | 2 | 1 | UK | 19.11.2013 10:23:22
2 | 1 | 2 | FR | 20.11.2013 06:04:22
tbl_customer: id_customer | name | adress | zip | city
1 | Frank|Street X| 211 | London
2 | Steve|Street Y| 431 | Paris
tbl_customer: id_product| Price | product
1 | 100,00| Hammer
2 | 50,00| Saw
Let's start with a very simple star schema model; for example, I assumed you don't need to worry about handling changes to dimensions' attributes.
factSales
DateKey
CustomerKey
ProductKey
Counter (=1; this is a factless fact table)
dimDate
DateKey
Date
Year
Quarter
Month
...
dimCustomer
CustomerKey
Name
Address
Zip
City
dimProduct
ProductKey
Name
Price (if it changes, you need move it to factSales)
How many people bought product x.
SELECT DISTINCT CustomerKey
FROM factSales
WHERE ProductKey IN ( SELECT ProductKey
FROM dimProduct
WHERE Name = 'Product X' )
How many sales are made from city x.
SELECT SUM(Counter)
FROM factSales
WHERE CustomerKey IN ( SELECT CustomerKey
FROM dimCustomer
WHERE City = 'City X' )
How many sales were made between Time x and y.
SELECT SUM(Counter)
FROM factSales
WHERE DateKey IN ( SELECT DateKey
FROM dimDate
WHERE Date BETWEEN DateX AND DateY )

Resources