Find errors in a sequence - mysql

I have an activity changelog of officers that become active/inactive.
OfficerID ChangeTo ChangeDate
1 active 2017-05-01
1 active 2017-05-02
1 inactive 2017-05-04
6 active 2013-09-09
6 inactive 2016-04-14
6 recruit 2016-06-22
6 active 2016-06-23
6 inactive 2017-04-30
In the case above, officer id 1 is active from 1st of May until the 4th of May.
This is essentially a 'housekeeping' task. The second row is not required and should be deleted. I would like to do this within a MySQL procedure that is linked an event on a schedule. I need a query that can identify these rows, but I'm not sure how.
In a previous system, I had looped through an ordered list and compared the current value against the previous row's value. I have read that loops in MySQL are not encouraged, so I'm trying to figure out how to do this with queries alone.
I tried the following:
SELECT
a.ActivityID, a.OfficerID, a.ChangeTo, a.ChangeDate
FROM
tbl_Officers_Activity as a
INNER JOIN tbl_Officers_Activity AS b
ON a.OfficerID = b.OfficerID
AND a.ChangeDate > b.ChangeDate
AND a.ChangeTo = b.ChangeTo
INNER JOIN tbl_Officers_Activity AS c
ON a.OfficerID = c.OfficerID
AND a.ChangeDate < c.ChangeDate
AND a.ChangeTo <> c.ChangeTo
ORDER BY
OfficerID,
ChangeDate;
I was hoping I could somehow embed the criteria I need into the joins, but I'm at a loss. any help would be greatly appreciated.

This is what you need
SQLFIddle Demo
select a1.officerid,a1.changedate,a1.changeto_a as changeto
From
(select a.officerid,a.changedate,max(a.changeto) as changeto_a,count(*) as rnk
from tbl_Officers_Activity a
inner join tbl_Officers_Activity b
on a.OfficerID=b.OfficerID
and a.ChangeDate>=b.ChangeDate
group by a.officerid,a.changedate) a1
left join
(select a.officerid,a.changedate,max(a.changeto) as changeto_b,count(*) +1 as rnk
from tbl_Officers_Activity a
inner join tbl_Officers_Activity b
on a.OfficerID=b.OfficerID
and a.ChangeDate>=b.ChangeDate
group by a.officerid,a.changedate) b1
on a1.officerid=b1.officerid
and a1.rnk=b1.rnk
where changeto_a = changeto_b
Explanation:
MySQL doesn't have row_Number function, so first I had to derive it. I used this query to get row_number, which is names as rnk in the query. Call the table a1.
(select a.officerid,a.changedate,max(a.changeto) as changeto_a,count(*) as rnk
from tbl_Officers_Activity a
inner join tbl_Officers_Activity b
on a.OfficerID=b.OfficerID
and a.ChangeDate>=b.ChangeDate
group by a.officerid,a.changedate)
Now as MySQL doesn't have LEAD function also, I derived it by using the above query again, and changing the rnk to rnk+1, calling it b1.
Now to replicate LEAD, I left joined a1 with b1
Now using a where clause to find same changeto, you can get your output.

My solution in the end was similar to what Utsav posted, however, after comparing his solution to mine, I found mine to be accurate, and his inaccurate.
For my solution, I create a temporary table for the initial ordered list with an auto incremental primary key. Then clone the temp table and join on itself with the primary pk equal to itself +1 and also equal to the status change.
my procedure ends up like this:
-- create temp table
DROP TABLE IF EXISTS tmp_act;
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_act (
AutoID INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ActivityID INT(11),
OfficerID INT(11),
ChangeTo text
);
-- temp data
insert into tmp_act (ActivityID,OfficerID,ChangeTo)
SELECT
a.ActivityID, a.OfficerID, a.ChangeTo
FROM
tbl_Officers_Activity as a
ORDER BY
OfficerID,
ChangeDate;
-- housekeeping
DROP TABLE IF EXISTS tmp_act1;
DROP TABLE IF EXISTS tmp_act2;
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_act1 AS (SELECT * FROM tmp_act);
CREATE TEMPORARY TABLE IF NOT EXISTS tmp_act2 AS (SELECT * FROM tmp_act);
SELECT
a.AutoID, a.ActivityID, a.OfficerID, a.ChangeTo
FROM
tmp_act1 as a
INNER JOIN tmp_act2 AS b
ON a.OfficerID = b.OfficerID
AND a.ChangeTo = b.ChangeTo
AND a.AutoID = b.AutoID+1;

Related

query make a count to 1 which does not satistfy the condition

i have a two table i want to know the number of person who are all assigned to project in each sector
CREATE TABLE first1( a int,projectname varchar(20));
INSERT INTO first1 VALUES
(1001,'crm'),
(1002,'iic'),
(1003,'abc'),
(1004,'sifty bank');
CREATE TABLE diff(b int,name varchar(20),p_id int );
INSERT INTO diff VALUES
(101,'priya',1001),
(102,'divya',1002),
(103,'sidhu',null),
(104,'shiva',null),
(105,'surya',1002);
Query:
select first1.projectname,count(*) from first1 left join diff on first1.a=diff.p_id group by
first1.projectname;
The output of this code is:
abc|1
crm|1
iic|2
sifty bank|1
The expected output is :
abc|0
crm|1
iic|2
sifty bank|0
The problem is count(*); it counts how many rows there are in each group - A project without any person assigned still counts as 1. Instead, you need to count() something from the left table, so null values are not taken into account:
select f.projectname, count(d.p_id) as cnt_diff
from first1 f
left join diff d on f.a = d.p_id
group by f.projectname;
Note that you can get the same result with a subquery:
select f.projectname,
(select count(*) from diff d where d.p_id = f.a) as cnt_diff
from first1 f

Returning Value where no rows returned

Table one includes delivery_id
Table two includes the name of store where delivery originated
Table two includes delivery_id and delivery_events
I'm trying to find specific delivery_id(s) where there is no delivery_event. The issue is that if there is no delivery_event, there is no data stored in table MAINDB_DELIVERY_EVENT.
What would be the best query to pull deliveries by date where there is no delivery_event - EXTRA would be to have that pull in as a separate column called Event_Status with value failed where there is no corresponding delivery event?
Here's where I am now which is not returning anything:
select MO.delivery_id,
DS.name,
MO.created_at
from MAINDB_ORDER MO
join maindb_store DS on DS.id = MO.store_id
where exists (
select ISNULL (MO.delivery_id
from MAINDB_DELIVERY_EVENT dwe
where dwe.id is null)
and MO.created_at between '2019-01-01' and '2019-12-31'
limit 1
Is this what you want?
select MO.delivery_id, DS.name, MO.created_at,
exists (select 1
from MAINDB_DELIVERY_EVENT dwe
where dwe.delivery_id = mo.delivery_id
) as delivery_flag
from MAINDB_ORDER MO join
maindb_store DS
on DS.id = MO.store_id
where MO.created_at between '2019-01-01' and '2019-12-31'
limit 1;
I am guessing that delivery_id is what links the tables. However, that is speculation because it is not clear from your question what links the tables.
You might try the LEFT JOIN ... IS NULL pattern. This will find items in your MAINDB_ORDER table that don't have any matches in MAINDB_DELIVERY_EVENT via the delivery_id column.
SELECT MO.delivery_id
FROM MAINDB_ORDER MO
LEFT JOIN MAINDB_DELIVERY_EVENT MDE ON MO.delivery_id = MDE.delivery_id
WHERE MDE.delivery_id IS NULL
This gets you the delivery_id values present in MO and absent in MDE. You can add your other WHERE criteria as you wish.
This is equivalent to
SELECT MO.delivery_id
FROM MAINDB_ORDER MO
WHERE MO.delivery_id NOT IN (
SELECT delivery_id FROM MAINDB_DELIVERY_EVENT
)
Again, you should add your WHERE clauses as needed.

Mysql Left Join of 3 tables and a one being joined twice

I need to do a join of 3 tables.
They all have an ID value, the issue is:
Table 1
Is the main one, name : cnpj_cnae and the ID is CNPJ_CNAE
Table 2
I need to get four columns from this one (Up to here I already got it).
The ID here is CNPJ_CNAE
Table 3
Here's the issue. For each ID value on table 1 I can have more than 1 even 10 on this table. I need to get 4 columns from ONLY THE FIRST TWO of this table.
The ID here is CNPJ_SOCIO
The finishing table would need to look like
ID INFOTABLE1 INFOTABLE2 INFOTABLE3ROW1 INFOTABLE3ROW2
I have tried some joins, but being a relative newbie with mysql I am suffering >.<
I have tried to do this:
CREATE TABLE cnpj_cnae_emp_test3 AS (
SELECT
`cnpj_cnae_test2`.*,
`cnpj_soci`.NOME_SOCIO,`cnpj_soci`.CNPJ_CPF_SOCI,`cnpj_soci`.ID_QUALIFICACAO_REP,`cnpj_soci`.DESC_QUALIFICACAO_REP,
`cnpj_emp_02`.MUNICIPIO,`cnpj_emp_02`.BAIRRO,`cnpj_emp_02`.TIPO_LOGRADOURO,`cnpj_emp_02`.LOGRADOURO,`cnpj_emp_02`.NUMERO,`cnpj_emp_02`.COMPLEMENTO
FROM `cnpj_cnae_test2`, `cnpj_soci`, `cnpj_emp_02`
WHERE `cnpj_cnae_test2`.CNPJ_CNAE = `cnpj_soci`.CNPJ_SOCIO AND `cnpj_cnae_test2`.CNPJ_CNAE = `cnpj_emp_02`.CNPJ
);
The issue is this will generate extra rows for every SOCI where instead of 1 soci on each one I need 2 socis on each line.
Table 1:
Layout of CNAE table
Table 2:
Layout of EMP table
Table 3:
Layout of Socio table
The resulting table would look like this:
More detailed end result Final layout with descriptions
Layout of result
In the end this by Owl was exactly what I needed:
WITH soci_partition AS (
SELECT CNPJ_SOCIO
,NOME_SOCIO
,CNPJ_CPF_SOCIO
,ID_QUALIFICACAO
,DESC_QUALIFICACAO_SOCIO
,row_number() OVER (PARTITION BY CNPJ_SOCIO ORDER BY IDX ASC) AS rownum
FROM cnpj_soci
)
SELECT * -- add actual columns
FROM cnpj_cnae_test2 AS cnae
INNER JOIN cnpj_emp_02 AS emp -- left join instead? depends on table structure
ON cnae.ID_CNAE = emp.IDX
LEFT JOIN soci_partition AS soci1
ON soci1.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci1.rownum = 1
LEFT JOIN soci_partition AS soci2
ON soci2.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci2.rownum = 2
Not sure if I got the joins/partition right, the foreign keys weren't clear.
Even if it's not perfect, hopefully this explains the general theory. Please edit in your correct answer if you end up debugging.
Also don't use old-style joins. You'll immediately fail interviews on that alone; they were deprecated 25 years ago.
WITH soci_partition AS (
SELECT CNPJ_SOCIO
,NOME_SOCIO
,CNPJ_CPF_SOCIO
,ID_QUALIFICACAO
,DESC_QUALIFICACAO_SOCIO
,row_number() OVER (PARTITION BY CNPJ_SOCIO ORDER BY IDX ASC) AS rownum
FROM cnpj_soci
)
SELECT * -- add actual columns
FROM cnpj_cnae_test2 AS cnae
INNER JOIN cnpj_emp_02 AS emp -- left join instead? depends on table structure
ON cnae.ID_CNAE = emp.IDX
LEFT JOIN soci_partition AS soci1
ON soci1.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci1.rownum = 1
LEFT JOIN soci_partition AS soci2
ON soci2.CNPJ_SOCIO = cnae.CNPJ_CNAE and soci2.rownum = 2
Give a try to below query
select * from cnae t1
left join emp t2 on t1.cnpj_cnae = t2.cnpj
left join socio t3 on t1.cnpj_cnae = t2.cnpj_socio
group by idx_cnae ;

fetch data employee table with 1 id and multiple transaction id

here in transaction table one employee have multiple transaction .i want all transaction of 1 specific employee .but employee id should be print once in column .here is format
1.here one employee have more than one transaction id .id should display 1 time and display all transaction
desired result: employee details should display in one row but transaction id in multiple row
error id repeating id:
Copied from comments:
select
b.TransactionId,
a.Name
from
TransactionRecharge b
left JOIN Customer a on a.Id=b.CustomerId
where
b.CustomerId=101282
This is not a Good practice, I am pretty sure that "I am going to get negative marks for helping you", Anyway i just did for what you asked.
Below Query using ROW_NUMBER will help you get what you need exactly.
CREATE TABLE #customer(Id INT PRIMARY KEY,Name VARCHAR(255))
CREATE TABLE #TransactionRecharge(TransactionId VARCHAR(255),CustomerId INT FOREIGN KEY REFERENCES #customer(id))
INSERT INTO #customer
VALUES (1,'Raj'),(2,'Bala'),(3,'Chandra')
INSERT INTO #TransactionRecharge
VALUES ('reansaction1',1),('reansaction2',1),
('reansaction3',1),('reansaction4',1),
('reansaction5',1),('reansaction6',1),
('reansaction7',1),('reansaction8',2)
SELECT
CASE
WHEN ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY b.TransactionId,a.Id) = '1' THEN CONVERT(VARCHAR,a.Id)
ELSE ''
END AS Customer,
CASE
WHEN ROW_NUMBER() OVER ( PARTITION BY a.id ORDER BY b.TransactionId,a.Id) = '1' THEN Name
ELSE ''
END AS Name,
b.TransactionId
FROM
#TransactionRecharge b
LEFT JOIN
#customer a ON a.Id = b.CustomerId
WHERE 1 = 1
--AND b.CustomerId = 1
DROP TABLE #customer
DROP TABLE #TransactionRecharge

How to left join or inner join a table itself

I have this data in a table, for instance,
id name parent parent_id
1 add self 100
2 manage null 100
3 add 10 200
4 manage null 200
5 add 20 300
6 manage null 300
How can I left join or inner join this table itself so I get this result below?
id name parent
2 manage self
4 manage 10
6 manage 20
As you can I that I just want to query the row with the keyword of 'manage' but I want the column parent's data in add's row as the as in manage's row in the result.
Is it possible?
EDIT:
the simplified version of my actual table - system,
system_id parent_id type function_name name main_parent make_accessible sort
31 30 left main Main NULL 0 1
32 31 left page_main_add Add self 0 1
33 31 left page_main_manage Manage NULL 0 2
my actual query and it is quite messy already...
SELECT
a.system_id,
a.main_parent,
b.name,
b.make_accessible,
b.sort
FROM system AS a
INNER JOIN -- self --
(
SELECT system_id, name, make_accessible, sort
FROM system AS s2
LEFT JOIN -- search --
(
SELECT system_id AS parent_id
FROM system AS s1
WHERE s1.function_name = 'page'
) AS s1
ON s1.parent_id = s2.parent_id
WHERE s2.parent_id = s1.parent_id
AND s2.system_id != s1.parent_id
ORDER BY s2.sort ASC
) b
ON b.system_id = a.parent_id
WHERE a.function_name LIKE '%manage%'
ORDER BY b.sort ASC
result I get currently,
system_id main_parent name make_accessible sort
33 NULL Main 0 1
but I am after this,
system_id main_parent name make_accessible sort
33 self Main 0 1
You just need to reference the table twice:
select t1.id, t1.name, t2.id, t2.name
from TableA t1
inner join TableA t2
on t1.parent_id = t2.Id
Replace inner with left join if you want to see roots in the list.
UPDATE:
I misread your question. It seems to me that you always have two rows, manage one and add one. To get to "Add" from manage:
select system.*, (select parent
from system s2
where s2.parent_id = system.parent_id
and s2.name = 'add')
AS parent
from system
where name = 'manage'
Or, you might split the table into two derived tables and join them by parent_id:
select *
from system
inner join
(
select * from system where name = 'add'
) s2
on system.parent_id = s2.parent_id
where system.name = 'manage'
This will allow you to use all the columns from s2.
Your data does not abide to a child-parent hierarchical structure. For example, your column parent holds the value 10, which is not the value of any id, so a child-parent association is not possible.
In other words, there's nothing that relates the record 2,manage,null to the record 1,add,self, or the record 4,manage,null to 3,add,10, as you intend to do in your query.
To represent hierarchical data, you usually need a table that has a foreign key referencing it's own primary key. So your column parent must reference the column id, then you can express a child-parent relationship between manage and add. Currently, that's not possible.
UPDATED: Joining by parent_id, try:
select m.id, m.name, a.parent
from myTable m
join myTable a on m.parent_id = a.parent_id and a.name = 'add'
where m.name = 'manage'
Change the inner join to a left join if there may not be a corresponding add row.