I know that there may be a solution elsewhere but everywhere I look, the solutions posted provide no help to me. I know what I call basic SQL but this is the first time coming to this problem and using CASE.
SELECT CASE
WHEN EXISTS (SELECT id FROM map WHERE map="mp" AND showMap="1")
THEN (SELECT file,id FROM map WHERE map="mp" AND showMap="1" ORDER BY date DESC)
ELSE (SELECT file,id FROM map WHERE map="mp" ORDER BY date ASC LIMIT 1)
END
By what I have found so far, Error: ER_OPERAND_COLUMNS: Operand should contain 1 column(s) means that I can only select one item instead of the two that I want. How can I change this code to let me select both the id and file from the map that I want.
To explain the code more, if map mp has a version that has showMap=1 then select all of those maps, else if map mp does not have a map with showMap=1 then select the 1st map
There will always be at least one map mp.
You can only select one column at a time from the data.
I am thinking this does what you want:
SELECT file, id
FROM map
WHERE map = 'mp'
ORDER BY (showMap = '1') DESC,
date ASC
LIMIT 1;
This prioritizes the values using ORDER BY, putting the row(s) (if any) with showMap = '1' first and then ordering by date. The query returns the first row.
EDIT:
If you want all the maps with showMap = '1', then something a bit more complex:
(SELECT file, id
FROM map
WHERE map = 'mp' AND showMap = '1'
) UNION ALL
(SELECT file, id
FROM map
WHERE map = 'mp' AND
NOT EXISTS (SELECT 1
FROM map m2
WHERE m2.map = 'mp' and m2.showMap = '1'
)
ORDER BY date ASC
LIMIT 1
);
Related
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
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
(SELECT schemename, message FROM RandomMessagesSet where type = 'ES' ORDER BY RAND())
UNION ALL (SELECT schemename, message FROM RandomMessagesSet where type = 'HE' ORDER BY RAND()) ORDER BY schemename;
This gives the list of all the messages with their scheme names. Is there a way to get 3 each of type "ES" and 2 each of type "HE" for each of schemename?
This is not a homework but part of a research problem that would feed into designing a user study. I tried using LIMIT and JOIN by looking at most of the posts here but still stuck.
Please help me. Your help would assist me to design my second last experiment for my PhD.
EDIT: Thanks to the most empathetic person who downvoted this. You should try doing a PhD yourself to get the feel of it.
I'm afraid I cannot provide sample data due to nature of research work.
Desired output:
In MySQL 8.0 and above, we can use Window Functions. We use a Partition over an expression of concatenated string of schemename and type.
Try (DB Fiddle):
SELECT dt.schemename,
dt.type,
dt.message
FROM
(
SELECT
schemename,
type,
message,
ROW_NUMBER() OVER (PARTITION BY CONCAT(schemename, '-', type)
ORDER BY RAND()) AS row_num
FROM RandomMessagesSet
) AS dt
WHERE (dt.type = 'ES' AND dt.row_num <= 3) OR
(dt.type = 'HE' AND dt.row_num <= 2)
ORDER BY dt.schemename
how could we remove or not displaying duplicate row with some conditional clause in sqlserver query, the case look like this one,
code decs
-------------------------
G-006 New
G-006 Re-Registration
how can we display just G-006 with Re-Registration Desc, i have tried with this query but no luck either
with x as (
select new_registration_no,category,rn = row_number()
over(PARTITION BY new_registration_no order by new_registration_no)
from equipment_registrations
)
select * from x
By using the same field in the PARTITION BY and ORDER BY clause, the rn field will always equal 1.
Assuming that new_registration_no = code and category = decs, you could change the ORDER BY field to be ORDER BY category DESC to get that result. However, that's a pretty arbitrary ORDER BY - you're just basing it on a random text value. I'm also not 100% sure how well the ROW_NUMBER() function works in a CTE.
A better solution might be something like:
SELECT *
FROM
(
SELECT
New_Registration_No,
Category,
ROW_NUMBER() OVER
(
PARTITION BY New_Registration_No
ORDER BY
CASE
WHEN Category = 'Re-Registration' THEN 1
WHEN Category = 'New' THEN 2
ELSE 3
END ASC ) rn
FROM Equipment_Registrations
) s
WHERE rn = 1
You can set the order in the CASE statement to be whatever you want - I'm afraid that without more information, that's the best solution I can offer you. If you have a known list of values that might appear in that field, it should be easy; if not, it will be a little harder to configure, but that will be based on business rules that you did not include in your original post.
I got specific query:
SELECT *
FROM stats
WHERE mod_name = 'module'
GROUP BY domain
ORDER BY addition_date DESC
I want to retrive, the newest value for every domain. I know there is a domain x.com value with date 2014-02-19.
However, this query returns me row with date: 2014-01-06
That's quite simple query... why it does not take group by domains and take only newest value?
Am I missing something?
The order by takes place after the group by. That is why your query does not work. Here is a way to get what you want:
SELECT s.*
FROM stats s
WHERE mod_name = 'module' and
not exists (select 1
from stats s2
where s2.mod_name = s.mod_name and
s2.addition_date > s.addition_date
)
ORDER BY addition_date DESC;
To get the best performance, create an index on stats(mod_name, addition_date).
I have a table like this (MySQL 5.0.x, MyISAM):
response{id, title, status, ...} (status: 1 new, 3 multi)
I would like to update the status from new (status=1) to multi (status=3) of all the responses if at least 20 have the same title.
I have this one, but it does not work :
UPDATE response SET status = 3 WHERE status = 1 AND title IN (
SELECT title FROM (
SELECT DISTINCT(r.title) FROM response r WHERE EXISTS (
SELECT 1 FROM response spam WHERE spam.title = r.title LIMIT 20, 1)
)
as u)
Please note:
I do the nested select to avoid the famous You can't specify target table 'response' for update in FROM clause
I cannot use GROUP BY for performance reasons. The query cost with a solution using LIMIT is way better (but it is less readable).
EDIT:
It is possible to do SELECT FROM an UPDATE target in MySQL. See solution here
The issue is on the data selected which is totaly wrong.
The only solution I found which works is with a GROUP BY:
UPDATE response SET status = 3
WHERE status = 1 AND title IN (SELECT title
FROM (SELECT title
FROM response
GROUP BY title
HAVING COUNT(1) >= 20)
as derived_response)
Thanks for your help! :)
MySQL doesn't like it when you try to UPDATE and SELECT from the same table in one query. It has to do with locking priorities, etc.
Here's how I would solve this problem:
SELECT CONCAT('UPDATE response SET status = 3 ',
'WHERE status = 1 AND title = ', QUOTE(title), ';') AS sql
FROM response
GROUP BY title
HAVING COUNT(*) >= 20;
This query produces a series of UPDATE statements, with the quoted titles that deserve to be updated embedded. Capture the result and run it as an SQL script.
I understand that GROUP BY in MySQL often incurs a temporary table, and this can be costly. But is that a deal-breaker? How frequently do you need to run this query? Besides, any other solutions are likely to require a temporary table too.
I can think of one way to solve this problem without using GROUP BY:
CREATE TEMPORARY TABLE titlecount (c INTEGER, title VARCHAR(100) PRIMARY KEY);
INSERT INTO titlecount (c, title)
SELECT 1, title FROM response
ON DUPLICATE KEY UPDATE c = c+1;
UPDATE response JOIN titlecount USING (title)
SET response.status = 3
WHERE response.status = 1 AND titlecount.c >= 20;
But this also uses a temporary table, which is why you try to avoid using GROUP BY in the first place.
I would write something straightforward like below
UPDATE `response`, (
SELECT title, count(title) as count from `response`
WHERE status = 1
GROUP BY title
) AS tmp
SET response.status = 3
WHERE status = 1 AND response.title = tmp.title AND count >= 20;
Is using GROUP BY really that slow ? The solution you tried to implement looks like requesting again and again on the same table and should be way slower than using GROUP BY if it worked.
This is a funny peculiarity with MySQL - I can't think of a way to do it in a single statement (GROUP BY or no GROUP BY).
You could select the appropriate response rows into a temporary table first then do the update by selecting from that temp table.
you'll have to use a temporary table:
create temporary table r_update (title varchar(10));
insert r_update
select title
from response
group
by title
having count(*) < 20;
update response r
left outer
join r_update ru
on ru.title = r.title
set status = case when ru.title is null then 3 else 1;