what is an efficient query for this task - mysql

I have a data file that looks like this
item_id | status
1 | null
2 | null
2 | new
3 | new
4 | null
4 | new
5 | null
Notice that item 2 and 4 have both 2 status: null and new.
I want to create a query that would extract only item_id with 1 status, which is null. So, i want my query to extract only 1 and 5.
I ended up doing this, but this doesn't look efficient:
1.List items with null status
create table query_1 as
select * from table1 where status = 'null';
2.List item with new status
create table query_2 as
select * from table1 where status = 'new';
3.select all results from query 1, excluding any id found from the results of query 2
select * from query_1 where item_id not in (select item_id from query_2)
Am I over thinking this? Is there a simpler query that can accomplish this?

First you have to check for null values with IS NULL. =null or ='null' won't work.
SELECT item_id, MAX(status)
FROM table1
GROUP BY item_id
HAVING MAX(status) IS NULL

select item_id
from table1
group by item_id
having count(*) = 1 and status is null

You can use self-join. What you need to do is remove the item_id's having new values. The remaining entries would be would you want. So you can frame your query as such :
SELECT item_id,
status
FROM tableName
WHERE item_id NOT IN (SELECT item_id
FROM #tab
WHERE status = 'new')
You can see this here -> SQL Fiddle Example

SELECT item_id FROM items
WHERE status IS NOT NULL
AND item_id NOT IN
(SELECT item_id FROM items
WHERE status IS NULL
)

You could use DISTINCT in this instance
SELECT DISTINCT item_id FROM items WHERE status IS NULL;

Related

Count rows where value in row is also in previous row

I want to get a count where the contents of a value in one row is also in the previous row.
Row | Item1 | Item2 | Item 3 |
1 | Dog | Cat | Rat
2 | Bird | Cat | Horse
3 | Horse | Dog | Rat
4 | Bird | Cat | Horse
5 | Horse | Bird | Cat
Row 2 would increase the count of Cat because Cat is in row 1 and 2
Row 3 would increase the count of Horse because Horse is also in Row 2
Row 4 would increase the count of Horse because Horse is also in Row 3
Row 5 would increase the count of Horse AND Cat because both of those appear in row 4.
There can be a max of 100 items or SKU's and I can index on any or all fields. At any given time there's probably between 1000 and 2000 rows.
I can't even wrap my head around where to begin with this query other than "SELECT * FROM table WHERE"
First, create table with all available unique values of SKU:
CREATE TABLE results(
id VARCHAR(255) NOT NULL PRIMARY KEY
);
-- All fields should be listed here one-by-one.
INSERT IGNORE INTO results (select Item1 from example);
INSERT IGNORE INTO results (select Item2 from example);
INSERT IGNORE INTO results (select Item3 from example);
Previous row could be obtained by left join primary table again with itself, i.e. LEFT JOIN example AS previous ON previous.id + 1 = example.id.
After that we've to check that each unique result exists in example table within current row and in previous row and finally get this:
SELECT
r.*,
SUM(
CASE WHEN r.id IN (
prv.Item1, prv.Item2, prv.Item3 -- All fields should be listed here.
) THEN 1 ELSE 0 END
) AS total
FROM
results AS r
LEFT JOIN
example AS cur ON r.id IN (
cur.Item1, cur.Item2, cur.Item3 -- All fields should be listed here.
)
LEFT JOIN
example AS prv ON prv.id + 1 = cur.id
GROUP BY
r.id
ORDER BY
cur.id
;
See working example http://www.sqlfiddle.com/#!9/7ebd85/1/0
This can be done with window functions (available in MySQL 8.0).
An option is to unpivot the resultset, and then use lag() to check the previous record. Assuming that ids are always increasing by 1, you can do:
select
item,
sum(case when id = lag_id + 1 then 1 else 0 end) cnt_consecutive
from (
select
t.*,
lag(id) over(partition by item order by id) lag_id
from (
select id, item1 item from mytable
union all select id, item2 from mytable
union all select id, item3 from mytable
) t
) t
group by item
order by item
If you don't have an incremented column, you can generate one with dense_rank():
select
item,
sum(case when new_id = lag_new_id + 1 then 1 else 0 end) cnt_consecutive
from (
select
t.*,
lag(new_id) over(partition by item order by new_id) lag_new_id
from (
select
t.*,
dense_rank() over(order by id) new_id
from (
select id, item1 item from mytable
union all select id, item2 from mytable
union all select id, item3 from mytable
) t
) t
) t
group by item
order by item
In this DB Fiddle, both queries return:
item | cnt_consecutive
:---- | --------------:
Bird | 1
Cat | 2
Dog | 0
Horse | 3
Rat | 0
I see #frost-nzcr4 suggestion is very good and I was doing my own version quite similar to that yesterday. However, the approach I'm doing is a bit different because I didn't create a table specifically to store the unique value. Instead, I was doing similarly like #GMB UNION sub-query and it end up to be something like this :
SELECT B.row, A.allitem,
SUM(CASE WHEN A.allitem IN (C.Item1, C.Item2, C.Item3) THEN 1
ELSE 0 END) AS total
FROM
-- this sub-query will be dynamic and UNION will eliminate any duplicate
(SELECT item1 AS allitem FROM mytable UNION
SELECT item2 FROM mytable UNION
SELECT item3 FROM mytable) AS A
LEFT JOIN mytable AS B ON A.allitem IN (B.Item1, B.Item2, B.Item3)
LEFT JOIN mytable AS C ON C.row + 1 = B.row
GROUP BY A.allitem
ORDER BY B.row;
Fiddle here : https://www.db-fiddle.com/f/bUUEsaeyPpAMfR2bK1VpBb/2
As you can see this is exactly similar query to frost's suggestion with only minor modification. In the sub-query allitem value will be updated as long as there are new values inserted so you don't need to keep inserting new unique data into a separate table.
Also, this query would normally get this is incompatible with sql_mode=only_full_group_by error on MySQL v5.7 above unless you remove the sql_mode.

How to write select query along with IF condition in place of column name in mysql query

SELECT DISTINCT o.receipt,
if(SELECT status FROM list WHERE receipt = o.receipt GROUP BY receipt) as status
FROM orderlist o
What is the correct way to write the above query If condition and get result like below example.
Same receipt(orderId) has more than one tuple(row) and all this tuple might have different status value.And I want to set value as per receipt(orderId) first tuple status using IF statement.
IF(status = 'shipped', "Yes", "NO");
If you data model looks like this (ie you have a way of distinguishing the order of events) then a limit in your subquery might do.
drop table if exists t,t1;
create table t(id int);
create table t1(id int, tid int, status varchar(10));
insert into t values (1),(2);
insert into t1 values (1,1,'a'),(2,1,'Shipped'),(3,1,'Returned'), (4,2,'shipped');
select t.id,
if(
(select status from t1 where t1.tid = t.id order by id limit 1)
= 'Shipped','yes','no') shipped
from t;
Result
+------+---------+
| id | shipped |
+------+---------+
| 1 | no |
| 2 | yes |
+------+---------+
2 rows in set (0.00 sec)
But isn't shipment usually the last status?
Try this.
SELECT DISTINCT o.receipt, CASE status when 'Shipped' then 'Yes' ELSE 'No' END as status
FROM orderlist o join receipt r on o.receipt = r.receipt

MySQL how to count from GROUP BY

date | userid | companyid
12.8.14 | 1 | 1
12.8.14 | 2 | 2
12.8.14 | 3 | 1
I had a table like above. It is easy to count how many company 1 from table with normal query.
My question is : if my query is select * from table where companyid = '1' group by date, how can i get mysql_num_row equal to 2 for company 1 and userid is 1 and 3?
select * from table where companyid = '1' group by date will only return me
mysql_num_row equal 1 and result 12.8.14 | 1 | 1
You can nest a query to get the sum of company one entries and then join that nested query to an outer query:
SELECT ABB2.*, ABB1.mysql_num_row
FROM `table` AS ABB2
JOIN
(SELECT companyid, COUNT(userid) AS mysql_num_row
FROM `table`
GROUP BY companyid) AS ABB1 ON ABB1.companyid = ABB2.companyid
WHERE ABB2.companyid = 1;
Example
try like this also
select *,(select count(*) from table1 where companyid=a.companyid) as count
from t as a where companyid=1
You wanted:
select date,companyid,count(*)
from table
where userid = 1
group by date,companyid

counting with conditions in mysql

im a bit stuck in a quite simple query
i have these 4 columns in the table
id1 (unique), id2 (can repeat) , updated (boolean), updated_on
what i want now is to get a summary in the form
id2, count of updates , max (updated_on)
in short i want the result sorted by the recent updated_on while counting all rows for this id2 where update=1
and for ids that donot have any update=1, show 0, with the max updated_on
______________________
id2|_count__|___date__
1 | 0 | 11/03/05,
3 | 5 | 11/03/04,
6 | 3 | 11/03/03,
2 | 0 | 11/03/02,
i used this query :
select id2, count(updated),max(updated_on) from table
where updated=1
group by need_id_to
but this query doesnot bring results where count would be 0 (for obvious reasons because im adding a condition in the where clause)
A boolean field is 1 for true and 0 for false.
You can use this to get the count of all updated = true rows.
SELECT
id2
, SUM(updated) as updates
,MAX(updated_on) as last_update
FROM table1
GROUP BY id2
ORDER BY last_update DESC
select id2,
count(case
when updated=1 then updated
else null
end) updates_count,
max(updated_on) last_updated
from table
group by id2
order by last_updated desc
select id2,
SUM(CASE WHEN updated=1 THEN 1 ELSE 0 END) AS UpdatedCount,
max(updated_on)
from table
group by id2

MySQL merge amounts in a 2 rows

I'm looking to create a sql statement that will update a large set of data.
What I have is a table like
id, transid, amount, narative1, narative 2, total, active
1 1234 23.2 NULL NULL NULL 1
2 1234 120.33 NULL NULL NULL 1
3 1235 98.00 NULL NULL NULL 1
When there are two rows with the same transid I need to total them put the result in the total column of the first one with that transid and put the second amount in naritive2 of the first instance as well as make the second one inactive. It should ignore single rows for a transid.
The result of what I want to do should be:
id, transid, amount, narative1, narative 2, total, active
1 1234 23.2 NULL 120.33 143.53 1
2 1234 120.33 NULL NULL NULL 0
3 1235 98.00 NULL NULL NULL 1
I know a bit of a thong twister but..
Ideally I'd like to do this in just a MySQL statements. So I don't mind having to do multiple sql statements but I want to avoid connecting it to PHP etc. Its a very large set of data.
This will update only those transactions that have exactly 2 rows (not 1 and not 3 or more).
UPDATE mytable mtu
JOIN (
SELECT minid, maxid, mtmin.amount AS minamt, mtmax.amount AS maxamt
FROM (
SELECT MIN(id) AS minid, MAX(id) AS maxid
FROM mytable mti
GROUP BY
transid
HAVING COUNT(*) = 2
) mt
JOIN mytable mtmin
ON mtmin.id = minid
JOIN mytable mtmax
ON mtmax.id = maxid
) mts
ON id IN (minid, maxid)
SET narative2 = CASE id WHEN minid THEN minamt ELSE NULL END,
total = CASE id WHEN minid THEN minamt + maxamt ELSE NULL END,
active = (id = minid)