Cannot create materialized view using SELECT query with PER PARTITION LIMIT - cassandra

Table:
CREATE TABLE IF NOT EXISTS table (
a TEXT,
b TEXT,
c BIGINT,
PRIMARY KEY ((a, b), c)
) WITH CLUSTERING ORDER BY (c DESC);
I need to get only one record from each (a, b) partition for the entire selection where c will be in DESC order and b in ASC order:
SELECT * FROM table WHERE a='a-1' ORDER BY b ASC PER PARTITION LIMIT 1 ALLOW FILTERING;
Result:
ORDER BY is only supported when the partition key is restricted by an EQ or an IN.
I tried create materialized view for ordering by b:
CREATE MATERIALIZED VIEW IF NOT EXISTS table_view AS
SELECT a, b, c
FROM table
WHERE a IS NOT NULL AND b IS NOT NULL AND c IS NOT NULL
PER PARTITION LIMIT 1
PRIMARY KEY (a, b, c)
WITH CLUSTERING ORDER BY (b ASC, c DESC);
I get an error while creating on PER PARTITION LIMIT.
Is it really possible to do this? Or maybe there is some workaround for this case?

I'll try to explain why Scylla (and Cassandra) do not support the things you tried to do.
In Scylla (and Cassandra), partition keys are not ordered in any useful way - they are ordered by a hash function of the partition key, not by the partition key itself. In your case, the partition key is (a, b) - that is - the full pair. The restriction WHERE a='...' may match a million different partitions with partition keys ('...', b) for a million different b's, and these are not ordered by b's... Not only are they not ordered by b's - they aren't even colocated on the same node. The only way for Scylla to implement the WHERE a='...' restriction is to do a full-table scan across the entire cluster. This is why you had to add ALLOW FILTERING.
But even then, there is no O(N) way to implement the ORDER by b and this is why Scylla refuses to do it. As I said above, the query WHERE a='...' may return a million different partitions (a, b). Scylla would need to collect those million results, sort them all, and return them ordered by b. It can't do that. Scylla can scan an already-sorted partition (this is what the error message is telling you), but not sort unsorted results.
You can argue that Scylla could do in this case what search engines do, namely - do not sort the full result list up-front (O(nlogn) complexity, O(n) space), but rather collect only the top K results while scanning the entire table. But that makes paging through the entire result set inefficient - Scylla would need to do the entire scan for each page. That's not something that Scylla does in any other case.
Finally, for the materialized view, there is a different problem. You're right that PER PARTITION LIMIT is not supported there. There is a real problem to implement that. Imagine the following scenario:
You add an item with key a=1, b=1, c=1 to the base table. It is added to the view as well.
You add an item with key a=1, b=1, c=2. Because of the per-partition limit, and there is already an item with the same partition key (a=1,b=1), this new item is not inserted into the view.
You now delete the item with key a=1, b=1, c=1. It is deleted from the view as well, but now Scylla needs to figure out that it needs to add a=1,b=1,c=2 to the view because there is now room for this item in the per-partition limit.
Step 3 is difficult and inefficient, so Scylla does not currently support this use case.

Your query is invalid. As the error states, you can only use the ORDER BY clause if you specify the partition key.
In your case, the partition key is (a, b) -- not just column a, but BOTH a AND b. You cannot use ORDER BY on column b because it is part of the partition key.
You can however ORDER BY c because it is a clustering column and not part of the partition key.
SELECT ... FROM table
WHERE a = ?
AND b = ?
ORDER BY c ...
Note that in this example, BOTH a and b are restricted by the equality (EQ) operator. Cheers!

Related

Do I have to specify the partition key and all clustering keys in every query?

For example, if my primary key is a and clustering columns are b and c.
Can I only use the following in where condition?
select * from table where a = 1 and b = 2 and c = 3
Or are there any other queries that I can use?
I want to use
select * from table where a=1
and
select * from table where a = 1 and b = 2 and c = 3 and d = 4
Is that possible?
If not, then how can I model my data to make this possible?
Cassandra has lots of advantages, but it does not fit for every need.
Cassandra is a good choice, when you need to handle large amount of writes. People like it, because Cassandra is easily scalable, can handle huge datasets and highly fault tolerant.
You need to keep in mind that with Cassandra (if you really want to utilize it) the basic rule is to model your data to fit your queries. Don't model around relations. Don't model around objects. Model around your queries. This way you can minimize partition reads.
And of course you can query not just the primary keys and partition columns. You can:
add secondary index to some columns or
use the ALLOW FILTERING keyword
but of course, these are not that effective as having a well-modeled table.
For example, if my primary key is a and clustering columns are b and c.
So this translates into a definition of: PRIMARY KEY ((a),b,c). Based on that...
are there any other queries that I can use?
Yes. Some important points to understand; is that the query's WHERE clause with PRIMARY KEYs:
Must be specified in order.
Cannot be skipped.
Can be omitted, as long as the keys prior to it are specified.
select * from table where a=1
Yes, this query will work. That's because you're still querying by your partition key (a).
select * from table where a = 1 and b = 2 and c = 3 and d = 4
However, this will not work. That is because d is not (based on my understanding of your first statement) a part of your PRIMARY KEY definition.
If not, then how can I model my data to make this possible?
As Andrea mentioned, you should build your table according to the queries it needs to support. So if you need to query by a, b, c, and d, you'll need to make d a part of your PRIMARY KEY.

Delete lots of rows from a very large Cassandra Table

I have a table Foo with 4 columns A, B, C, D. The partitioning key is A. The clustering key is B, C, D.
I want to scan the entire table and find all rows where D is in set (X, Y, Z).
Then I want to delete these rows but I don't want to "kill" Cassandra (because of compactions), I'd like these rows deleted with minimal disruption or risk.
How can I do this?
You have a big problem here. Indeed, you really can't find the rows without actually scanning all of your partitions. The problem real problem is that C* will allow you to restrict your queries with a partition key, and then by your cluster keys in the order in which they appear in your PRIMARY KEY table declaration. So if your PK is like this:
PRIMARY KEY (A, B, C, D)
then you'd need to filter by A first, then by B, C, and only at the end by D.
That being said, for the part of finding your rows, if this is something you have to run only once, you
Could scan all your table and do comparisons of D in your App logic.
If you know the values of A you could query every partition in parallel and then compare D in your application
You could attach a secondary index and try to exploit speed from there.
Please note that depending on how many nodes do you have 3 is really not an option, secondary indexes don't scale)
If you need to perform such tasks multiple times, I'd suggest you to create another table that would satisfy this query, something like PRIMARY KEY (D), you'd then just scan three partitions and that would be very fast.
About deleting your rows, I think there's no way to do it without triggering compactions, they are part of C* and you have to live with them. If you really can't tolerate tombstone creation and/or compactions, the only alternative is to not delete rows from a C* cluster, and that often means thinking about a new data model that won't need deletes.

Where and Order By Clauses in Cassandra CQL

I am new to NoSQL database and have just started using apache Cassandra. I created a simple table "emp" with primary key on "empno" column. This is a simple table as we always get in Oracle's default scott schema.
Now I loaded data using the COPY command and issued query Select * from emp order by empno but I was surprised that CQL did not allow Order by on empno column (which is PK). Also when I used Where condition, it did not allow any inequality operations on empno column (it said only EQ or IN conditions are allowed). It also did not allowed Where and Order by on any other column, as they were not used in PK, and did not have an index.
Can someone please help me what should I do if I want to keep empno unique in the table and want a query results in Sorted order of empno?
(My version is:
cqlsh:demodb> show version
[cqlsh 5.0.1 | Cassandra 2.2.0 | CQL spec 3.3.0 | Native protocol v4]
)
There are two parts to a PRIMARY KEY in Cassandra:
partition key(s)
clustering key(s)
PRIMARY KEY (partitionKey1,clusteringKey1,clusteringKey2)
or
PRIMARY KEY ((partitionKey1,partitionKey2),clusteringKey1,clusteringKey2)
The partition key determines which node(s) your data is stored on. The clustering key determines the order of the data within your partition key.
In CQL, the ORDER BY clause is really only used to reverse the defined sort direction of your clustering order. As for the columns themselves, you can only specify the columns defined (and in that exact order...no skipping) in your CLUSTERING ORDER BY clause at table creation time. So you cannot pick arbitrary columns to order your result set at query-time.
Cassandra achieves performance by using the clustering keys to sort your data on-disk, thereby only returning ordered rows in a single read (no random reads). This is why you must take a query-based modeling approach (often duplicating your data into multiple query tables) with Cassandra. Know your queries ahead of time, and build your tables to serve them.
Select * from emp order by empno;
First of all, you need a WHERE clause. It's ok to query without it, if you're working with a relational database. With Cassandra, you should do your best to avoid unbound SELECT queries. Besides, Cassandra can only enforce a sort order within a partition, so querying without a WHERE clause won't return data in the order you want, anyway.
Secondly, as I mentioned above, you need to define clustering keys. If you want to order your result set by empno, then you must find another column to define as your partition key. Try something like this:
CREATE TABLE emp_by_dept (
empno text,
dept text,
name text,
PRIMARY KEY (dept,empno)
) WITH CLUSTERING ORDER BY (empno ASC);
Now, I can query employees by department, and they will be returned to me ordered by empno:
SELECT * FROM emp_by_dept WHERE dept='IT';
But to be clear, you will not be able to query every row in your table, and have it ordered by a single column. The only way to get meaningful order into your result sets, is first partition your data in a way that makes sense to your business case. Running an unbound SELECT will return all of your rows (assuming that the query doesn't time-out while trying to query every node in your cluster), but result set ordering can only be enforced within a partition. So you have to restrict by partition key in order for that to make any sense.
My apologies for self-promoting, but last year I wrote an article for DataStax called We Shall Have Order!, in which I addressed how to solve these types of problems. Give it a read and see if it helps.
Edit for additional questions:
From your answer I concluded 2 things about Cassandra:
(1) There is no
way of getting a result set which is only order by a column that has
been defined as Unique.
(2) When we define a PK
(partition-key+clustering-key), then the results will always be order
by Clustering columns within any fixed partition key (we must restrict
to one partition-key value), that means there is no need of ORDER BY
clause, since it cannot ever change the order of rows (the order in
which rows are actually stored), i.e. Order By is useless.
1) All PRIMARY KEYs in Cassandra are unique. There's no way to order your result set by your partition key. In my example, I order by empno (after partitioning by dept). – Aaron 1 hour ago
2) Stopping short of saying that ORDER BY is useless, I'll say that its only real use is to switch your sort direction between ASC and DESC.
I created an index on "empno" column of "emp" table, it is still not
allowing ORDER BY empno. So, what Indexes are for? are they only for
searching records for specific value of index key?
You cannot order a result set by an indexed column. Secondary indexes are (not the same as their relational counterparts) really only useful for edge-case, analytics-based queries. They don't scale, so the general recommendation is not to use secondary indexes.
Ok, that simply means that one table cannot be used for getting
different result sets with different conditions and different sorting
order.
Correct.
Hence for each new requirement, we need to create a new table.
IT means if we have a billion rows in a table (say Sales table), and
we need sum of sales (1) Product-wise, (2) Region-wise, then we will
duplicate all those billion rows in 2 tables with one in clustering
order of Product, the other in clustering order of Region,. and even
if we need to sum sales per Salesman_id, then we build a 3rd table,
again putting all those billion rows? is it sensible?
It's really up to you to decide how sensible it is. But lack of query flexibility is a drawback of Cassandra. To get around it you can keep creating query tables (I.E., trading disk for performance). But if it gets to a point where it becomes ungainly or difficult to manage, then it's time to think about whether or not Cassandra is really the right solution.
EDIT 20160321
Hi Aaron, you said above "Stopping short of saying that ORDER BY is useless, I'll say that its only real use is to switch your sort direction between ASC and DESC."
But i found even that is not correct. Cassandra only allows ORDER by in the same direction as we define in the "CLUSTERING ORDER BY" caluse of CREATE TABLE. If in that clause we define ASC, it allows only order by ASC, and vice versa.
Without seeing an error message, it's hard to know what to tell you on that one. Although I have heard of queries with ORDER BY failing when you have too many rows stored in a partition.
ORDER BY also functions a little odd if you specify multiple columns to sort by. If I have two clustering columns defined, I can use ORDER BY on the first column indiscriminately. But as soon as I add the second column to the ORDER BY clause, my query only works if I specify both sort directions the same (as the CLUSTERING ORDER BY definition) or both different. If I mix and match, I get this:
InvalidRequest: code=2200 [Invalid query] message="Unsupported order by relation"
I think that has to do with how the data is stored on-disk. Otherwise Cassandra would have more work to do in preparing result sets. Whereas if it requires everything to either to match or mirror the direction(s) specified in the CLUSTERING ORDER BY, it can just relay a sequential read from disk. So it's probably best to only use a single column in your ORDER BY clause, for more predictable results.
Adding a redux answer as the accepted one is quite long.
Order by is currently only supported on the clustered columns of the PRIMARY KEY
and when the partition key is restricted by an Equality or an IN operator in where clause.
That is if you have your primary key defined like this :
PRIMARY KEY ((a,b),c,d)
Then you will be able to use the ORDER BY when & only when your query has :
a where clause with all the primary key restricted either by an equality operator (=) or an IN operator such as :
SELECT * FROM emp WHERE a = 1 AND b = 'India' ORDER BY c,d;
SELECT * FROM emp WHERE a = 1 AND b = 'India' ORDER BY c;
These two query are the only valid ones.
Also this query would not work :
SELECT * FROM emp WHERE a = 1 AND b = 'India' ORDER BY d,c;
because order by currently only support the ordering of columns following their declared order in the PRIMARY KEY that is in primary key definition c has been declared before d and the query violates the ordering by placing d first.

Cassandra - querying on clustering keys

I am just getting start on Cassandra and I was trying to create tables with different partition and clustering keys to see how they can be queried differently.
I created a table with primary key of the form - (a),b,c where a is the partition key and b,c are clustering key.
When querying I noticed that the following query:
select * from tablename where b=val;
results in:
Cannot execute this query as it might involve data filtering and thus
may have unpredictable performance. If you want to execute this query
despite the performance unpredictability, use ALLOW FILTERING
And using "ALLOW FILTERING" gets me what I want (even though I've heard its bad for performance).
But when I run the following query:
select * from tablename where c=val;
It says:
PRIMARY KEY column "c" cannot be restricted (preceding column "b" is either not restricted or by a non-EQ relation)
And there is no "ALLOW FILTERING" option at all.
MY QUESTION IS - Why are all clustering keys not treated the same? column b which is adjacent to the partition key 'a' is given an option of 'allow filtering' which allows querying on it while querying on column 'c' does not seem possible at all (given the way this table is laid out).
ALLOW FILTERING gets cassandra to scan through all SSTables and get the data out of it when the partition key is missing, then why cant we do the same column c?
It's not that clustering keys are not treated the same, it's that you can't skip them. This is because Cassandra uses the clustering keys to determine on-disk sort order within a partition.
To add to your example, assume PRIMARY KEY ((a),b,c,d). You could run your query (with ALLOW FILTERING) by specifying just b, or b and c. But it wouldn't allow you to specify c and d (skipping b) or b and d (skipping c).
And as a side node, if you really want to be able to query by only b or only c, then you should support those queries with additional tables designed as such. ALLOW FILTERING is a band-aid, and is not something you should ever do in a production Cassandra deployment.

Cassandra has a limit of 2 billion cells per partition, but what's a partition?

In Cassandra Wiki, it is said that there is a limit of 2 billion cells (rows x columns) per partition. But it is unclear to me what is a partition?
Do we have one partition per node per column family, which would mean that the max size of a column family would be 2 billion cells * number of nodes in the cluster.
Or will Cassandra create as much partitions as required to store all the data of a column family?
I am starting a new project so I will use Cassandra 2.0.
With the advent of CQL3 the terminology has changed slightly from the old thrift terms.
Basically
Create Table foo (a int , b int, c int, d int, PRIMARY KEY ((a,b),c))
Will make a CQL3 table. The information in a and b is used to make the partition key, this describes which node the information will reside on. This is the 'partiton' talked about in the 2 billion cell limit.
Within that partition the information will be organized by c, known as the clustering key. Together a,b and c, define a unique value of d. In this case the number of cells in a partition would be c * d. So in this example for any given pair of a and b there can only be 2 billion combinations of c and d
So as you model your data you want to ensure that the primary key will vary so that your data will be randomly distributed across Cassandra. Then use clustering keys to ensure that your data is available in the way you want it.
Watch this video for more info on Datmodeling in cassandra
The Datamodel is Dead, Long live the datamodel
Edit: One more example from the comments
Create Table foo (a int , b int, c int, d int, e int, f int, PRIMARY KEY ((a,b),c,d))
Partitions will be uniquely identified by a combination of a and b.
Within a partition c and d will be used to order cells within the partition so the layout will
look a little like:
(a1,b1) --> [c1,d1 : e1], [c1,d1 :f1], [c1,d2 : e2] ....
So in this example you can have 2 Billion cells with each cell containing:
A value of c
A value of d
A value of either e or f
So the 2 billion limit refers to the sum of unique tuples of (c,d,e) and (c,d,f).
From : http://www.datastax.com/documentation/cql/3.0/cql/cql_reference/create_table_r.html
Using a composite partition key¶
A composite partition key is a partition key consisting of multiple columns. You use an extra set of parentheses to enclose columns that make up the composite partition key. The columns within the primary key definition but outside the nested parentheses are clustering columns. These columns form logical sets inside a partition to facilitate retrieval.
CREATE TABLE Cats (
block_id uuid,
breed text,
color text,
short_hair boolean,
PRIMARY KEY ((block_id, breed), color, short_hair)
);
For example, the composite partition key consists of block_id and breed. The clustering columns, color and short_hair, determine the clustering order of the data. Generally, Cassandra will store columns having the same block_id but a different breed on different nodes, and columns having the same block_id and breed on the same node.
Implication
==> Partition is the smallest unit of replication (which on its own makes sh** no sense. :) )
==> Every combination of block_id and breed is a Partition.
==> On any given machine in cluster, either all or none of the rows with same partition-key will exist.

Resources