MySQL - multiple case when for varchars - mysql

I am trying to use CASE WHEN to count when specific text is withing the varchar column. I have been trying to crack it but I have an issue when second, third CASE WHEN is added.
The code I use and works is:
SELECT *,
CASE
WHEN (Orders.Some_text LIKE "%test%" AND Orders.Some_text NOT LIKE "%found%") THEN 1
ELSE 0 END AS Test
FROM Orders;
Now I add the second CASE WHEN:
SELECT *,
CASE
WHEN (Orders.Some_text LIKE "%test%" AND Orders.Some_text NOT LIKE "%found%") THEN 1
WHEN (Orders.Some_text LIKE "%test%" AND Orders.Some_text NOT LIKE "%missing%") THEN 1
ELSE 0 END AS Test
FROM Orders;
And it produces errors in the Test column.
Results I want are just simple 1 when word test is found and it is without found, missing or/and during.
+------+--------------+------------+
| id | Some_text | Test |
+------+--------------+------------+
| 1 | test | 1 |
| 2 | test found | 0 |
| 3 | found test | 0 |
| 4 | test missing | 0 |
| 5 | missing test | 0 |
| 6 | test during | 0 |
| 7 | during test found | 0 |
| 8 | abc | 0 |
+------+--------------+------------+
The code to reproduce my dataset:
CREATE TABLE Orders
(
id INT,
Some_text char(255));
insert into Orders values (1, "test");
insert into Orders values (2, "test found");
insert into Orders values (3, "found test");
insert into Orders values (4, "test missing");
insert into Orders values (5, "miss ing test");
insert into Orders values (6, "test during");
insert into Orders values (7, "during test found");
insert into Orders values (8, "abc");

It seems you need to check if both the words found and missing aren't there in the field, then set the value as 1.
Combine the statements like below and it should return expected output.
SELECT *,
CASE
WHEN (Orders.Some_text LIKE "%test%" AND
Orders.Some_text NOT LIKE "%found%" AND
Orders.Some_text NOT LIKE "%missing%") THEN 1
ELSE 0 END AS Test
FROM Orders;
Output
+------+--------------+------------+
| id | Some_text | Test |
+------+--------------+------------+
| 1 | test | 1 |
| 2 | test found | 0 |
| 3 | found test | 0 |
| 4 | test missing | 0 |
| 5 | missing test | 0 |
| 6 | test during | 1 |
| 7 | during test found | 0 |
| 8 | abc | 0 |

Related

Mysql insert multiple rows, if duplicate value column then update

I have a MySQL table that looks like this
id | customer_id | textkey | value |
---+-------------+---------+-------+
1 | 1 | text1 | value1|
2 | 1 | text2 | value2|
3 | 1 | text3 | value3|
4 | 1 | text4 | value4|
5 | 2 | text1 | value1|
...
I want to insert the following values to customer_id 1
(text1, valueX), (text3, valueY), (text5, value5), (text6, value6)
The end result should look like this
mysql> SELECT * from MyTable where customer_id=1;
id | customer_id | textkey | value |
---+-------------+---------+-------+
1 | 1 | text1 | valueX|
2 | 1 | text2 | value2|
3 | 1 | text3 | valueY|
4 | 1 | text4 | value4|
6 | 1 | text5 | value5|
7 | 1 | text6 | value6|
The value column should update whenever there is a duplicate in the textkey insert, and insert regularly if the textkey is not a duplicate. What query could I use to achieve this? I tried using IGNORE, LAST_INSERT_ID(), and ON DUPLICATE KEY UPDATE but I cant figure out what works for a "ON DUPLICATE COLUMN UPDATE" scenario. Thanks
You must create unique index which will detect the duplication:
CREATE UNIQUE INDEX idx ON test (customer_id, textkey);
Now you can use INSERT .. ODKU:
INSERT INTO test (customer_id, textkey, value) VALUES
(1, 'text1', 'valueX'),
(1, 'text3', 'valueY'),
(1, 'text5', 'value5'),
(1, 'text6', 'value6')
ON DUPLICATE KEY UPDATE
value = VALUES(value);
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=6dcc351706f574f4c9f13c1fc95b9225

How to find value of a row based on values between start and ending condition?

I have been working on a table to get the end timestamp of a play (nowPlaying) event based on the following conditions:
If there is a browseSearch between nowPlaying and browseFind, then the case is ignored.
browseFind would be the starting point for each of the nowPlaying event.
There could be any number of other events between a browseFind and nowPlaying.
What I have done is tried to partition the query and add another column with Start, Break, Skip and end as shown in table below:
The events are ordered by timestamp event_ts
+------+--------------+-------+---------------------+
| id | page_type | p_t | event_ts |
+------+--------------+-------+---------------------+
| 1 | browseFind | Start | 2021-01-01 06:00:00 |
| 1 | browseSearch | break | 2021-01-01 06:00:10 |
| 1 | browseFind | Start | 2021-01-01 06:01:00 |
| 1 | x-ray | skip | 2021-01-01 06:01:30 |
| 1 | browseSearch | break | 2021-01-01 06:02:00 |
| 1 | nowPlaying | end | 2021-01-01 06:03:00 |
| 1 | browseFind | Start | 2021-01-01 06:10:00 |
| 1 | abc | skip | 2021-01-01 06:11:00 |
| 1 | abc | skip | 2021-01-01 06:12:00 |
| 1 | nowPlaying | end | 2021-01-01 06:12:00 |
+------+--------------+-------+---------------------+
The only valid timestamp would be the last one 2021-01-01 06:12:00 because rest have break in between their Start and End. Also if there is no end and a start follows, then the loop will reset.
I saw a bunch of post on SO, but it was not done in SQL, but rather programmatically.
How can I use the p_t column to write a SQL that will look at events between the start and end if it exists and get the valid timestamp of end
My Query:
select id,
page_type,
case when page_type in ('browseFind') then 'Start'
when page_type in ('browseSearch') then 'break'
when page_type not in ('browseFind','nowPlaying') then 'skip'
when page_type in ('nowPlaying') then 'end'
end as p_t,
event_ts
from steps
order by event_ts;
Queries for DDL:
create table steps (id int, page_type varchar(100), event_ts timestamp);
insert into steps values (1,'browseFind','2021-01-01 6:00:00');
insert into steps values (1,'browseSearch','2021-01-01 6:00:10');
insert into steps values (1,'browseFind','2021-01-01 6:01:00');
insert into steps values (1,'x-ray','2021-01-01 6:01:30');
insert into steps values (1,'browseSearch','2021-01-01 6:02:00');
insert into steps values (1,'nowPlaying','2021-01-01 6:03:00');
insert into steps values (1,'browseFind','2021-01-01 6:10:00');
insert into steps values (1,'abc','2021-01-01 6:11:00');
insert into steps values (1,'abc','2021-01-01 6:12:00');
insert into steps values (1,'nowPlaying','2021-01-01 6:12:00');
Do you need in this:
WITH
cte AS ( SELECT id, page_type, event_ts,
LAG(page_type) OVER (PARTITION BY id ORDER BY event_ts) lag_page_type
FROM steps
WHERE page_type IN ('browseFind', 'browseSearch', 'nowPlaying') )
SELECT id, page_type, event_ts
FROM cte
WHERE page_type = 'nowPlaying'
AND lag_page_type = 'browseFind';
fiddle

How to use CASE function in ORDER BY?

My friend asked a question a few times ago. Also there is a answer under that and it is good, but not for my case. The idea of that solution is joining the current table to itself. That seems expensive and not effective for me, Because in reality there is four join on these tables (votes, favorites, comments, viewed) in my query.
Now I want to know, how can I do that using CASE function? Something like this:
... ORDER BY Type, CASE WHEN AcceptedAnswerId = Id THEN 1 ELSE 0, timestamp
Or is there any better solution?
To be more readable, I paste those examples here:
I have a table like this:
// Mytable
+----+--------------------+------+------------------+-----------+
| Id | QuestionOrAnswer | Type | AcceptedAnswerId | timestamp |
+----+--------------------+------+------------------+-----------+
| 1 | question1 | 0 | 3 | 1 |
| 2 | answer1 | 1 | NULL | 2 |
| 3 | answer2 | 1 | NULL | 3 | -- accepted answer
| 4 | answer3 | 1 | NULL | 4 |
+----+--------------------+------+------------------+-----------+
Now I want this result: (please focus on the order)
+----+--------------------+------+------------------+-----------+
| Id | QuestionOrAnswer | Type | AcceptedAnswerId | timestamp |
+----+--------------------+------+------------------+-----------+
| 1 | question1 | 0 | 3 | 1 |
| 3 | answer2 | 1 | NULL | 3 | -- accepted answer
| 2 | answer1 | 1 | NULL | 2 |
| 4 | answer3 | 1 | NULL | 4 |
+----+--------------------+------+------------------+-----------+
// ^ 0 means question and 1 means answer
CASE would work, but you are missing the END. But in this case, you could also just use IF(AcceptedAnswerId = Id,1,0).
In the simple case you show, you could just do:
order by type,if(type=0,(#accepted:=acceptedanswerid),id<>#accepted),timestamp
but I don't know if that would work in your real case.
Given the table definition (without proper indices) + sample data
CREATE TABLE Table1
(`Id` int, `QuestionOrAnswer` varchar(9), `Type` int, `AcceptedAnswerId` varchar(4), `related` int NOT NULL, `timestamp` int)
;
INSERT INTO Table1
(`Id`, `QuestionOrAnswer`, `Type`, `AcceptedAnswerId`, `related`, `timestamp`)
VALUES
(1, 'question1', 0, '3', 1, 1),
(2, 'answer1', 1, NULL, 1, 2),
(3, 'answer2', 1, NULL, 1, 3),
(4, 'answer3', 1, NULL, 1, 4)
you can use the query
SELECT
t2.*
FROM
table1 as t1
JOIN
table1 as t2
ON
t1.related=t2.related
WHERE
t1.related = 1
AND t1.Type = 0
ORDER BY
t2.Type desc, t2.Id=t1.AcceptedAnswerId, t2.Id
to get the question/answer set of a specific question (t1.related = 1 <- adjust that parameter for other questions).
And no, with the right indices this query is not "expensive".
example at http://sqlfiddle.com/#!9/24954/4 (yeah, took me 4 attempts to get it right, grrrrr)

Populate values from one table

I am trying to populate an empty table(t) from another table(t2) based on a flag field being set. He is my attempt below and the table data.
UPDATE 2014PriceSheetIssues AS t
JOIN TransSalesAvebyType2013Combined AS t2
SET t.`Tran_Type`=t2.`Tran_Type` WHERE t.`rflag`='1';
When I run the script, I receive (0) zero records affected.??
+-----------+----------------+-------------------+-------+-------+
| Tran_Type | RetailAvePrice | WholesaleAvePrice | Rflag | Wflag |
+-----------+----------------+-------------------+-------+-------+
| 125C | 992 | 650 | 1 | NULL |
| 2004R | 1500 | NULL | 1 | NULL |
| 4EAT | 1480 | 1999 | 1 | 1 |
+-----------+----------------+-------------------+-------+-------+
I think you should just do the following
INSERT INTO 2014PriceSheetIssues
( `fldX`, `fldY` )
VALUES (
SELECT `fldX`, `fldY`
FROM TransSalesAvebyType2013Combined
WHERE 2014PriceSheetIssues.`rflag`='1'
)
The select query gets the values and the insert puts it in the (empty) other table.

Getting count of insert/update rows from ON DUPLICATE KEY UPDATE

I have a statement that tries to insert a record and if it already exists, it simply updates the record.
INSERT INTO temptable (col1,col2,col3)
VALUES (1,2,3)
ON DUPLICATE KEY UPDATE col1=VALUES(col1), col2=VALUES(col2), col3=VALUES(col3);
The full statement has multiple inserts and I'm looking to count number of INSERTs against the UPDATEs. Can I do this with MySQL variables, I've yet to find a way to do this after searching.
From Mysql Docs
In the case of "INSERT ... ON DUPLICATE KEY UPDATE" queries, the return value will be 1 if an insert was performed, or 2 for an update of an existing row.
Use mysql_affected_rows() after your query, if INSERT was performed it will give you 1 and if UPDATE was performed it will give you 2.
I've accomplished what you're describing using a while loop so that each iteration creates a MySQL statement that affects one row. Within the loop, I run the mysql_affected_rows() and then increment a counter depending upon whether the value returned was a 0 or a 1. At the end of the loop, I echo both variables for viewing.
The complete wording from MySQL Docs regarding the mysql_affected_rows function is (notice there are 3 possible values returned - 0, 1, or 2):
For INSERT ... ON DUPLICATE KEY UPDATE statements, the affected-rows
value per row is 1 if the row is inserted as a new row, 2 if an
existing row is updated, and 0 if an existing row is set to its
current values. If you specify the CLIENT_FOUND_ROWS flag, the
affected-rows value is 1 (not 0) if an existing row is set to its
current values.
(Sidenote - I set $countUpdate and $countInsert and $countUpdateNoChange to 0 prior to the while loop):
Here's the code that I developed that works great for me:
while (conditions...) {
$sql = "INSERT INTO test_table (control_number, name) VALUES ('123', 'Bob')
ON DUPLICATE KEY UPDATE name = 'Bob'";
mysql_query($sql) OR die('Error: '. mysql_error());
$recordModType = mysql_affected_rows();
if ($recordModType == 0) {
$countUpdateNoChange++;
}elseif($recordModType == 1){
$countInsert++;
}elseif($recordModType == 2){
$countUpdate++;
};
};
echo $countInsert." rows inserted<br>";
echo $countUpdateNoChange." rows updated but no data affected<br>";
echo $countUpdate." rows updated with new data<br><br>";
Hopefully, I haven't made any typos as I've recreated it to share while removing my confidential data.
Hope this helps someone. Good luck coding!
I know this is a bit old, but I was doing a bulk insert in PHP and needed to know exactly how many rows were inserted and updated (separately).
So I used this:
$dataCount = count($arrData); // number of rows in the statement
$affected = mysql_affected_rows(); // mysqli_*, PDO's rowCount() or anything
$updated = $affected - $dataCount;
$inserted = 2 * $dataCount - $affected;
Simple trace table:
-------------------------------
| data | affected | ins | upd |
-------------------------------
| 1 | 1 | 1 | 0 |
-------------------------------
| 2 | 2 | 2 | 0 |
| 2 | 3 | 1 | 1 |
| 2 | 4 | 0 | 2 |
-------------------------------
| 3 | 3 | 3 | 0 |
| 3 | 4 | 2 | 1 |
| 3 | 5 | 1 | 2 |
| 3 | 6 | 0 | 3 |
-------------------------------
| 4 | 4 | 4 | 0 |
| 4 | 5 | 3 | 1 |
| 4 | 6 | 2 | 2 |
| 4 | 7 | 1 | 3 |
| 4 | 8 | 0 | 4 |
-------------------------------
| 5 | 5 | 5 | 0 |
| 5 | 6 | 4 | 1 |
| 5 | 7 | 3 | 2 |
| 5 | 8 | 2 | 3 |
| 5 | 9 | 1 | 4 |
| 5 | 10 | 0 | 5 |
-------------------------------
if you want to get the number of records that have been inserted and updated separetly, you are to issue each statement separetly.