Order query only by a specific field if all values from result set are NULL

Dennis :

I am building a custom document versioning system in my website.

For this, I have 2 tables "vrm_document" and "vrm_document_active_document". The first table contains all documents on my website, while the second table tells me which document is the active version for which user, so the second table contains the vrm_document_id field and a user_id field. Active documents are indicated by the parent_vrm_document_id being not NULL.

I have 2 kinds of documents:

  • Default documents
  • Documents uploaded by the users themselves

Each document that is uploaded contains versions, and min. 1 and max. 1 version has to be active at all times for the user. Thus the second table vrm_document_active_document will always contain the vrm_document_id combined with the user_id for the documents uploaded by the user himself.

However, the problem resides in the fact that default documents are documents that are provided by my website and will always be available to everyone. When a user does not have its own version of a default document, the default document will become the active version for that user by default. This is implicated. For active documents we do NOT insert rows in the vrm_document_active_document table. This means that if there are no versions of a default document active for the user, the default document becomes the active version by default for that user.

When displaying the documents with their versions to the user, I want to show all the versions in a list under each other, where the active version is displayed first, and then the others are sorted on the last modified time.

The following query gives me the correct results for documents that aren't default documents:

SELECT vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1  ) ) 
ORDER BY vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC

However, as can be seen from the query, if we imagine that document ID 1 to be a default document, all fields in the vrm_document_active_document table will contain NULL, and thus it will sort based on the modified time.

The following is (in pseudo-code) what I need to achieve, is this possible using mariaDB SQL?

ORDER BY:

  • if all values == NULL then value with default_document is first , then order by modified_utc
  • else order by modified_utc

DB Fiddle: https://www.db-fiddle.com/f/71TUvdxFgFLhdncgo4BRre/1

EDIT: DB Fiddle added

Lajos Arpad :

In MariaDB you have the ifnull function and you can use it as

ifnull(expr1, expr2)

which effectively says:

evaluate expr1 and if it's null, then evaluate expr2

If I understand your problem well, then your solution looks like

order by ifnull(vdad.vrm_document_id, ifnull(vd.timestamp_modified_utc, someothervalue)) desc

In the clause above someothervalue represents any field that you may want to default to. The desirable criteria was unclear to me. However, if you want the default document to be the very first criteria, then

ORDER BY dv.is_default_document desc, vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC

or, if you want that to be the very last criteria, then

ORDER BY vdad.vrm_document_id DESC, vd.timestamp_modified_utc DESC, dv.is_default_document desc

EDIT

SELECT vdad.*, vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1  ) ) 
ORDER BY 
CASE not exists (
    SELECT 1 as cnt
    FROM `vrm_document` AS `vd2`
    LEFT JOIN vrm_document_active_document AS vdad2 ON (vd2.vrm_document_id = vdad2.vrm_document_id AND vd2.user_id = vdad2.user_id)
    WHERE ((`vd2`.`parent_vrm_document_id` = 1 AND `vd2`.`user_id` IN (2,18,21)) OR (`vd2`.`vrm_document_id` = 1  ) ) 
    AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))
)
WHEN 1 THEN vd.vrm_document_id
ELSE 0 END,
vdad.vrm_document_id DESC, 
vd.timestamp_modified_utc DESC

The query above uses case-when-else-end to check for the existence of some records. It is unclear for me what should be the exact criteria, but, even if the example above is not your exact match, then you can replace

AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))

to what you need and it should work.

EDIT2

The current query sorts by the two criteria in the question by default, but if the first two columns are null, then the very first element will be the default document:

SELECT vdad.*, vd.*
FROM `vrm_document` AS `vd`
LEFT JOIN vrm_document_active_document AS vdad ON (vd.vrm_document_id = vdad.vrm_document_id AND vd.user_id = vdad.user_id)
WHERE ((`vd`.`parent_vrm_document_id` = 1 AND `vd`.`user_id` IN (2,18,21)) OR (`vd`.`vrm_document_id` = 1  ) ) 
ORDER BY 
CASE not exists (
    SELECT 1 as cnt
    FROM `vrm_document` AS `vd2`
    LEFT JOIN vrm_document_active_document AS vdad2 ON (vd2.vrm_document_id = vdad2.vrm_document_id AND vd2.user_id = vdad2.user_id)
    WHERE ((`vd2`.`parent_vrm_document_id` = 1 AND `vd2`.`user_id` IN (2,18,21)) OR (`vd2`.`vrm_document_id` = 1  ) ) 
    AND not ((vdad2.vrm_document_id is null) AND (vdad2.user_id))
)
WHEN 1 THEN vd.is_default_document
ELSE 0 END desc,
vdad.vrm_document_id DESC, 
vd.timestamp_modified_utc DESC

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=407678&siteId=1