SUBPARTITION BY LIST INSIDE LIST PARTITION in mariaDB - mariadb

ALTER TABLE ticket_details PARTITION BY LIST(ticket_status_id)
SUBPARTITION BY LIST(ticket_type_id)
(
PARTITION QTR1 VALUES IN (1)
(
SUBPARTITION sQTR1 VALUES IN (1),
SUBPARTITION sQTR2 VALUES IN (2)
),
PARTITION QTR2 VALUES IN (2)
(
SUBPARTITION s2QTR1 VALUES IN (1),
SUBPARTITION s2QTR2 VALUES IN (2)
),
PARTITION QTR3 VALUES IN (3)
(
SUBPARTITION s3QTR1 VALUES IN (1),
SUBPARTITION s3QTR2 VALUES IN (2)
),
PARTITION QTR4 VALUES IN (4),
(
SUBPARTITION s4QTR1 VALUES IN (1),
SUBPARTITION s4QTR2 VALUES IN (2)
),
PARTITION QTR5 VALUES IN (15)
(
SUBPARTITION s5QTR1 VALUES IN (1),
SUBPARTITION s5QTR2 VALUES IN (2)
),
PARTITION QTR6 VALUES IN (NULL)
(
SUBPARTITION snQTR1 VALUES IN (1),
SUBPARTITION snQTR2 VALUES IN (2)
),
PARTITION QTR7 VALUES IN (0)
(
SUBPARTITION s0QTR1 VALUES IN (1),
SUBPARTITION s0QTR2 VALUES IN (2)
)
);
I am trying to run this query to make subpartitions but it gives me this error. I have tried many combinations but nothing is working when i try subpartitions by list. Is it possible to do subpartition by list
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'LIST (ticket_type_id)

You cannot subpartition BY LIST. Both in MariaDB and in MySQL, SUBPARTITION types are limited to HASH and KEY.

Related

In MariaDB, how to select the last event in every day and create indexes for it?

I have a table of meetings, each of which has a start and end time expressed as an integer Unix timestamp, as well as a group of people who are attending this meeting. I need to send out notifications to each group of people once all their meetings for the day have completed. To do this I need to find the last meeting for every combination of (day, group). I've adapted this answer for my situation, and it works:
SELECT MAX(`starts_at`), `id`, `group_id`, DATE(FROM_UNIXTIME(`starts_at`)) `day`
FROM `meeting`
GROUP BY `day`, `group_id`;
However, despite trying out different combinations of indexes, I can't seem to find one that would make this query not perform a full-table scan. The EXPLAIN result is always as follows:
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
| 1 | SIMPLE | meeting | ALL | NULL | NULL | NULL | NULL | 16 | Using temporary; Using filesort |
+------+-------------+---------+------+---------------+------+---------+------+------+---------------------------------+
My table is defined this way:
CREATE TABLE `meeting` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL,
`starts_at` int(11) NOT NULL,
`ends_at` int(11) NOT NULL,
... other fields ...,
PRIMARY KEY (`id`),
CONSTRAINT `meeting_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `group` (`id`),
)
What combination of indexes, and what query, would I need here? One of the solutions I see is to create an indexed column that stores the event's day, perhaps as some sort of ordinal, and while this is an option I'd prefer to avoid it if possible in order to not have redundant values in a single row.
If you are running MySQL 8.0.13 or higher, you could try an index on expressions:
create index idx_meeting on meeting(
group_id,
(date(from_unixtime(starts_at))),
starts_at desc
);
This puts first the columns / expressions that appear in the group by clause, then the column that is aggregated; since we will be looking for the max(), we want to sort that column in descending order in the index.
In this DB Fiddle, it looks like the index is picked up by the database (but note that I have no data to play with - the results may be different in your environment:
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
meeting
null
index
idx_meeting
idx_meeting
12
null
1
100.00
Using index; Using temporary
A further option would be to simplify the query a little. We could arithmetics rather than timestamp conversion. We can easily put an index on this - or use a computed column if your version does not support indexes on expression:
alter table meeting
add starts_date date as (floor(starts_at / 60 / 60 / 24))
stored;
create index idx_meeting2 on meeting(group_id, starts_date, starts_at desc);
Then we run the query as follows:
SELECT MAX(starts_at), group_id, DATE(MAX(starts_at)) day
FROM meeting
GROUP BY group_id, starts_day;
Explain in Maria DB 10.3:
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
1
SIMPLE
meeting
index
null
idx_meeting2
12 null
1
Using index
Edit: you want the latest event per group and per day. Aggregation is not appropriate. Instead, we need to filter.
Consider the following set up:
alter table meeting
add starts_day int(11) as (floor(starts_at / 60 / 60 / 24) * 60 * 60 * 24)
stored;
create index idx_meeting on meeting(group_id, starts_day, starts_at desc);
Now we can use the following query:
select starts_at, group_id, date(from_unixtime(starts_day)) day
from meeting m
where starts_at = (
select max(m1.starts_at)
from meeting m1
where m1.group_id = m.group_id and m1.starts_day = m.starts_day
)
The subquery takes advantage of the index, that may also partially be used in the outer query.
Demo on DB Fiddle

How to cast a column into decimal of varying significant digits in Oracle

I have a column that is stored in ###0.0000000000 format. In a report I'm generating I need it to only show a few significant digits. Problem is the number needed changes based on the product with a default of 2. There's a column in another table that provides the required digits per each product.
I've tried a few things so far but it seems to not like it and throws a syntax error.
Cast(A.Price as Numeric(10,coalesce(B.Sig_Digits,2)))
That threw an error so I tried making the coalesce part a column and aliasing it in case the coalesce broke it, and that didn't work either. Round will take a column as an argument but I don't want it to round. Other than an ugly
case when Sig_digits = 1 then to_char(price,'###0.0') when Sig_digits = 2...
etc. what other options are there? This is a very large report, with 100+ columns and a few million rows so I'd prefer to not do the case when.
Use TO_CHAR with RPAD to add 0s to the end of the format model to the correct number of decimal places:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( value, sig ) AS
SELECT 123.456789, 2 FROM DUAL UNION ALL
SELECT 123456789.123456789, 7 FROM DUAL;
Query 1:
SELECT TO_CHAR( value, RPAD( 'FM999999999990.', sig + 15, '0' ) )
FROM table_name
Results:
| TO_CHAR(VALUE,RPAD('FM999999999990.',SIG+15,'0')) |
|---------------------------------------------------|
| 123.46 |
| 123456789.1234568 |

PL SQL SELECT Case Statements involving aggregate values

I'm trying to write a query that in Teradata but I'm not sure how to do it; my table looks like this:
col1: text (account_number)
col2: text (secondary account number)
col3: text (Primary_cust)
the business requirements are:
"Group records by account number.
If there is only one record for an account then keep that record.
If there are multiple records for an account number then:
(1) if only one record has Primary_CUST = 'Y' then keep.
(2) if multiple records have Primary_CUST = 'Y' then keep one with lowest SCDRY_ACCT_NBR
(3) If no records have Primary_CUST = 'Y' then keep one with lowest SCDRY_ACCT_NBR.
I know I need a CASE statement and I'm able to write the first requirement, but not sure on the second. Any help would be greatly appreciated.
You just have to think about how to order the rows to get the row you want on top, seems to be like this:
SELECT * FROM tab
QUALIFY
Row_Number()
Over (PARTITION BY account_number -- for each account
ORDER BY Primary_CUST DESC -- 'Y' before 'N' (assuming it's a Y/N column)
,SCDRY_ACCT_NBR -- lowest number
) = 1 -- return the top row
Of course QUALIFY is proprietary Teradata syntax, if you need to do this on Oracle you have to wrap it in a Derived Table:
SELECT *
FROM
(
SELECT t.*,
Row_Number()
Over (PARTITION BY account_number -- for each account
ORDER BY Primary_CUST DESC -- 'Y' before 'N' (assuming it's a Y/N column)
,SCDRY_ACCT_NBR) AS rn-- lowest number
FROM tab
) AS dt
WHERE rn = 1 -- return the top row

Oracle: Composite unique key with Date column

I have created a table with composite unique key as below--
create table test11
(
aa number,
bb varchar2(10),
cc DATE,
dd number,
ee NUMBER
);
CREATE UNIQUE INDEX TEST11_IDX ON TEST11 (AA,BB,CC);
Now, whenever I try to insert data, I get this error:
ORA-00001: unique constraint (CDUREFDB.TEST11_IDX) violated
INSERT INTO TEST11 VALUES (1, 'AA', SYSDATE, 1, 1);
commit;
INSERT INTO TEST11 VALUES (1, 'AA', SYSDATE, 1, 1);
commit;
Is that because of DATE column is considering Date value till seconds?
Because I could see below query is returning is result--
select to_char(CC,'DD-Mon-YY HH:Mi:SS AM') from test11;
TO_CHAR(CC,'DD-MON-YYHH:MI:SSAM')
---------------------------------
17-Mar-16 04:28:37 PM
17-Mar-16 04:28:43 PM
So, what can be done in order to only consider Date value (not hours, mins, secs precision) as unique key member.
Also, DATE column above(CC) has partition on it.
UPDATE::
In this table, we have RANGE partition on DATE column(CC).
And we are planning to remove partitions periodically (i.e. after some days interval).
So if I Don't use direct CC in unique index ( instead of making trunc as Justin suggested) then i am getting error as ORA-01502: index 'CDUREFDB.TEST111_IDX' or partition of such index is in unusable state if I try to insert data after some old partition got removed .
UPDATE_1
As per #Justin suggestion below, this issue is resolved creating virtual column like below:
CREATE TABLE TEST11
(
AA NUMBER,
BB VARCHAR2(10),
CC DATE,
DD NUMBER ,
EE NUMBER,
FF DATE generated always AS (TRUNC(CC)) virtual
)
PARTITION BY RANGE
(
FF
)
INTERVAL
(
NUMTODSINTERVAL(1,'DAY')
)
(
PARTITION partition_test_1 VALUES LESS THAN (TO_DATE('01-APR-2006','dd-MON-yyyy'))
);
CREATE UNIQUE INDEX TEST111_IDX ON TEST11 (AA,BB,FF) LOCAL; -- creating unique local index
A date always has a time component so your two rows have different cc values. You could create a function-based index based on the trunc(cc) value which will set the time component to midnight.
CREATE UNIQUE INDEX TEST11_IDX
ON TEST11 (AA,BB,trunc(CC));
Of course, that means that if you want a query to use the index, you'd want to ensure that your predicate is on trunc(cc) rather than cc.

How to UPDATE multiple columns using a correlated subquery in SQLite?

I want to update multiple columns in a table using a correlated subquery. Updating a single column is straightforward:
UPDATE route
SET temperature = (SELECT amb_temp.temperature
FROM amb_temp.temperature
WHERE amb_temp.location = route.location)
However, I'd like to update several columns of the route table. As the subquery is much more complex in reality (JOIN with a nested subquery using SpatiaLite functions), I want to avoid repeating it like this:
UPDATE route
SET
temperature = (SELECT amb_temp.temperature
FROM amb_temp.temperature
WHERE amb_temp.location = route.location),
error = (SELECT amb_temp.error
FROM amb_temp.temperature
WHERE amb_temp.location = route.location),
Ideally, SQLite would let me do something like this:
UPDATE route
SET (temperature, error) = (SELECT amb_temp.temperature, amb_temp.error
FROM amb_temp.temperature
WHERE amb_temp.location = route.location)
Alas, that is not possible. Can this be solved in another way?
Here's what I've been considering so far:
use INSERT OR REPLACE as proposed in this answer. It seems it's not possible to refer to the route table in the subquery.
prepend the UPDATE query with a WITH clause, but I don't think that is useful in this case.
For completeness sake, here's the actual SQL query I'm working on:
UPDATE route SET (temperature, time_distance) = ( -- (C)
SELECT -- (B)
temperature.Temp,
MIN(ABS(julianday(temperature.Date_HrMn)
- julianday(route.date_time))) AS datetime_dist
FROM temperature
JOIN (
SELECT -- (A)
*, Distance(stations.geometry,route.geometry) AS distance
FROM stations
WHERE EXISTS (
SELECT 1
FROM temperature
WHERE stations.USAF = temperature.USAF
AND stations.WBAN_ID = temperature.NCDC
LIMIT 1
)
GROUP BY stations.geometry
ORDER BY distance
LIMIT 1
) tmp
ON tmp.USAF = temperature.USAF
AND tmp.WBAN_ID = temperature.NCDC
)
High-level description of this query:
using geometry (= longitude & latitude) and date_time from the route table,
(A) find the weather station (stations table, uniquely identified by the USAF and NCDC/WBAN_ID columns)
closest to the given longitude/latitude (geometry)
for which temperatures are present in the temperature table
(B) find the temperature table row
for the weather station found above
closest in time to the given timestamp
(C) store the temperature and "time_distance" in the route table

Resources