Thomas Schwärzl
Thomas Schwärzl

Reputation: 9917

Raw query must include the primary key

I got a raw SQL statement in my views.py

Message.objects.raw('''
        SELECT s1.ID, s1.CHARACTER_ID, MAX(s1.MESSAGE) MESSAGE, MAX(s1.c) occurrences
        FROM
           (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
            FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s1
        LEFT JOIN
           (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
            FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s2
          ON s1.CHARACTER_ID=s2.CHARACTER_ID
         AND s1.c < s2.c
        WHERE s2.c IS NULL
        GROUP BY CHARACTER_ID
        ORDER BY occurrences DESC''', [days, days])

The result of this SQL statement (tested on database directly) is:

ID  | CHARACTER_ID | MESSAGE | OCCURENCES
----+--------------+---------+--------------
148 | 10           | test    | 133

But all I got is a InvalidQuery Exception with the information Raw query must include the primary key

Then I double checked the docs and read:

There is only one field that you can’t leave out - the primary key field....An InvalidQuery exception will be raised if you forget to include the primary key.

As you can see I got the requested primary key added in my statement. What's wrong?

class Message(models.Model):
    character = models.ForeignKey('Character')
    message = models.TextField()
    location = models.ForeignKey('Location')
    ts = models.DateTimeField()

    class Meta:
        pass

    def __unicode__(self):
        return u'%s: %s...' % (self.character, self.message[0:20])

Upvotes: 26

Views: 46885

Answers (4)

Muhammad Zahid
Muhammad Zahid

Reputation: 345

Make Sure the primary key is part of the select statement.

Example:

This will not work:

`Model.objects.raw("Select Min(id), rider_id from Table_Name group by rider_id")`

But this will work:

`Model.objects.raw("Select id, Min(id), rider_id from Table_Name group by rider_id")`

Upvotes: 5

typonaut
typonaut

Reputation: 323

For those also stuck with this problem, perhaps like me, wondering why Django needs a pk, when you don’t have a pk for the query (eg you want multiple rows) – Django just needs an id field returned, the pk does not need to be part of a where clause. ie:

select * from table where foo = 'bar';

or

select id, description from table where foo = 'bar';

Both of these work, if there is a field id in the table. But this throws the error described by Thomas Schwärzl, because no id field is returned:

select description from table where foo = 'bar';

Upvotes: 3

Tinashe Robert
Tinashe Robert

Reputation: 711

Include 1 as id to your query

Message.objects.raw('''
        SELECT 1 as id , s1.ID, s1.CHARACTER_ID, MAX(s1.MESSAGE) MESSAGE, MAX(s1.c) occurrences
        FROM
           (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
            FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s1
        LEFT JOIN
           (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
            FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s2
          ON s1.CHARACTER_ID=s2.CHARACTER_ID
         AND s1.c < s2.c
        WHERE s2.c IS NULL
        GROUP BY CHARACTER_ID
        ORDER BY occurrences DESC''', [days, days]) 

Upvotes: 32

dusan
dusan

Reputation: 9273

I reproduced the same problem using Python 2.7.5, Django 1.5.1 and Mysql 5.5.

I've saved the result of the raw call to the results variable, so I can check what columns it contains:

>>> results.columns
['ID', 'CHARACTER_ID', 'MESSAGE', 'occurrences']

ID is in uppercase, so in your query I changed s1.ID to s1.id and it works:

>>> results = Message.objects.raw('''
...         SELECT s1.id, s1.CHARACTER_ID, MAX(s1.MESSAGE) MESSAGE, MAX(s1.c) occurrences
...         FROM
...            (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
...             FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s1
...         LEFT JOIN
...            (SELECT ID, CHARACTER_ID, MESSAGE, COUNT(*) c
...             FROM tbl_message WHERE ts > DATE_SUB(NOW(), INTERVAL %s DAY) GROUP BY CHARACTER_ID,MESSAGE) s2
...           ON s1.CHARACTER_ID=s2.CHARACTER_ID
...          AND s1.c < s2.c
...         WHERE s2.c IS NULL
...         GROUP BY CHARACTER_ID
...         ORDER BY occurrences DESC''', [days, days])
>>> results.columns
['id', 'CHARACTER_ID', 'MESSAGE', 'occurrences']
>>> results[0]
<Message_Deferred_character_id_location_id_message_ts: Character object: hello...>

Upvotes: 19

Related Questions