MySQL SELECT following row with a different column value - mysql

I have a tabe like this:
sn(char) ts(int) data(char) flag(tinyint)
'a' 1494825611 'a0' 1
'a' 1494825613 'a1' 0
'a' 1494825617 'a2' 0
'a' 1494825623 'a3' 1
'a' 1494825634 'a4' 1
'b' 1494825644 'b1' 1
'b' 1494825643 'b0' 0
'a' 1494825645 'a5' 1
'a' 1494825648 'a6' 0
'b' 1494825658 'b2' 1
Rows may be in a wrong order (like b1 and b0), so they should be sorted by ts first.
I am trying to make an efficient query for sn to get rows where the current and the next flag differs.
As a result, I'd want something like this for sn 'a':
old_ts ts old_data data flag
1494825611 1494825613 'a0' 'a1' 0
1494825617 1494825623 'a2' 'a3' 1
1494825645 1494825648 'a5' 'a6' 0
and for sn 'b':
old_ts ts old_data data flag
1494825643 1494825644 'b0', 'b1' 1
It's not a problem to create additional columns or tables.

You can use #rowFlag variable. Each row check whether it's equals to flag. If yes set the filter field to 0 to skip it later
select old_ts, ts, old_data, data, new_flag as flag
from
(select
t.ts,
t.data,
case when #rowFlag=flag then 0 else 1 end as filter,
flag as new_flag,
#rowFlag:=flag as old_flag,
#old as old_data,
#old:=data,
#old_ts as old_ts,
#old_ts:=ts
from test t, (select #rowFlag:=-1, #old:=null, #old_ts:=null) as def
where sn='a'
order by ts) as sub
where filter=1 and old_ts is not null;
SQL Fiddle

You can check this, as this contains old value and new values as well. Change new_table with your actual table name.
select a.sn, a.ts as oldts, b.ts as newts,
a.data as old_data, b.data as data, a.flag as old_flag , b.flag as flag
from (
select sn, ts, data, flag ,
if(#oldSn = sn,#rowNumber := #rowNumber + 1,#rowNumber := 1) as row_number,
#oldSn := sn as curentsn
from new_table
order by sn, ts ) a
join (
select sn, ts, data, flag ,
if(#oldSn1 = sn,#rowNumber := #rowNumber + 1,#rowNumber := 1) as row_number,
#oldSn1 := sn as curentsn
from new_table
order by sn, ts ) b on a.sn = b.sn
and a.row_number + 1 = b.row_number
where a.flag != b.flag
Output of above query
sn, oldts, newts, old_data, data, old_flag, flag
a, 1494825611, 1494825613, a0, a1, 1, 0
a, 1494825617, 1494825623, a2, a3, 0, 1
a, 1494825645, 1494825648, a5, a6, 1, 0
b, 1494825643, 1494825644, b0, b1, 0, 1

Related

SQL-Print column name based on value and condition

I have been trying to print column names based on condition and value.
My problem is this
If two all the columns in the two rows like for data x and y have value yes the it should print those column names otherwise not
My code:
select 'A' from world.city where A = 'yes' AND data=y and data=x union all
select 'B' from world.city where B= 'yes' AND data=y and data=x union all
select 'C' from world.city where C= 'yes' AND data=y and data=x union all
select 'D' from world.city where D= 'yes' AND data=y and data=x union all
select 'E' from world.city where E= 'yes' AND data=y and data=x;
It is not giving perfect results.
Do a sum and union all then filter the rows with sum('yes') is equal to 2. See below.
select colname
from (
select 'A' as colname,sum(case when A='yes' then 1 else 0 end) col
from tbl where data in ('X','Y')
union all
select 'B',sum(case when B='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'C',sum(case when C='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'D',sum(case when D='yes' then 1 else 0 end)
from tbl where data in ('X','Y')
union all
select 'E',sum(case when E='yes' then 1 else 0 end)
from tbl where data in ('X','Y')) tab
where col = 2;
Result:
colname
B
D

How to Group and display count of each type of data?

Suppose I have a column which has three types of data like A, B, C. I want to group and count the number of each type.
For example if A is 3 times in column , B is 2 times and C is 1 time.
It should display as:
A B C
3 2 1
I would appreciate your help. Thankyou.
If you want the data in one row, you can use conditional aggregation:
select sum(col = 'a') as A, sum(col = 'b') as b, sum(col = 'c') as c
from t;
You can try the more explicit:
select sum(case when col = 'a' then 1 else 0 end) as A,
sum(case when col = 'b' then 1 else 0 end) as b,
sum(case when col = 'c' then 1 else 0 end) as c
from t;
I would actually suggest the following query:
SELECT
data, COUNT(*) AS cnt
FROM yourTable
GROUP BY data
This would generate counts in row form, e.g.
A 3
B 2
C 1
Most of the time this would meet your needs. If you instead really need columns you can try this:
SELECT
SUM(CASE WHEN data = 'A' THEN 1 ELSE 0 END) AS A,
SUM(CASE WHEN data = 'B' THEN 1 ELSE 0 END) AS B,
SUM(CASE WHEN data = 'C' THEN 1 ELSE 0 END) AS C
FROM yourTable
This would generate your literal expected output:
A B C
3 2 1

MySQL - Rows to Columns and keeps NULL

We have the following table (TEST2) in the MySQL database (MySQL 5.6):
TEAM_ID,MEMBER_ID,TYPE,SCORE
1,2,A,150
1,3,B,200
1,1,B,50
1,1,A,100
1,2,B,NULL
We try to transform/pivot the above table based on the TYPE column:
If the TYPE column has value == A, move the value in the SCORE column into a new column called A_SCORE. If the value in the SCORE column is NULL, it should show NULL in the new A_SCORE column.
If the TYPE column has value == B, move the value in the SCORE column into a new column called B_SCORE. If the value in the SCORE column is NULL, it should show NULL in the new B_SCORE column.
The following table is the one we are looking for (the wanted table):
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,100,50,50
1,2,150,NULL,NULL
1,3,0,200,-200
We tried the following query
SELECT TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,SUM(A_SCORE-B_SCORE) AS ACTUAL_MINUS_B_SCORE FROM
(SELECT TEAM_ID,MEMBER_ID,
CASE
WHEN SCORE IS NULL
THEN NULL
ELSE SUM(if(TYPE = 'A', SCORE,0) )
END A_SCORE,
CASE
WHEN SCORE IS NULL
THEN NULL
ELSE SUM(if(TYPE = 'B', SCORE,0) )
END B_SCORE
FROM TEST2
GROUP BY TEAM_ID,MEMBER_ID,SCORE) AS A
GROUP BY TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE);
It returns something we don’t want:
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,0,50,-50
1,1,100,0,100
1,2,0,0,0
1,2,150,0,150
1,3,0,200,-200
If we tried the following, it generates a table close to what we want, but it doesn’t return any NULL value.
SELECT TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,SUM(A_SCORE-B_SCORE) AS A_SCORE _MINUS_B_SCORE FROM
(SELECT TEAM_ID,MEMBER_ID,
SUM(if(TYPE = 'A', SCORE,0) ) AS A_SCORE,
SUM(if(TYPE = 'B', SCORE,0) )AS B_SCORE
FROM TEST2
GROUP BY TEAM_ID,MEMBER_ID) AS A
GROUP BY TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE;
The result of the above query:
TEAM_ID,MEMBER_ID,A_SCORE,B_SCORE,A_SCORE_MINUS_B_SCORE
1,1,100,50,50
1,2,150,0,0
1,3,0,200,-200
Could any guru enlighten how to generate the wanted table in this case using MySQL? The SQL fiddle is here for your convenience.
http://sqlfiddle.com/#!9/cfe7a1/1
Thanks!
Try this;)
SELECT TEAM_ID, MEMBER_ID, A_SCORE, B_SCORE, A_SCORE - B_SCORE AS A_SCORE_MINUS_B_SCORE
FROM (
SELECT
TEAM_ID, MEMBER_ID,
CASE
WHEN A_SCORE IS NULL AND NOT EXISTS (
SELECT 1 FROM TEST2
WHERE TEAM_ID = T1.TEAM_ID
AND MEMBER_ID = T1.MEMBER_ID
AND TYPE = 'A'
) THEN 0 ELSE A_SCORE END AS A_SCORE,
CASE
WHEN B_SCORE IS NULL AND NOT EXISTS (
SELECT 1 FROM TEST2
WHERE TEAM_ID = T1.TEAM_ID
AND MEMBER_ID = T1.MEMBER_ID
AND TYPE = 'A'
) THEN 0 ELSE B_SCORE END AS B_SCORE
FROM (
SELECT
TEAM_ID, MEMBER_ID,
MAX(CASE WHEN TYPE = 'A' THEN SCORE END) AS A_SCORE,
MAX(CASE WHEN TYPE = 'B' THEN SCORE END) AS B_SCORE
FROM TEST2
GROUP BY TEAM_ID, MEMBER_ID
) T1
)T
SQLFiddle demo here
I don't quite understand the calculation criteria, but something like this should work...
SELECT team_id
, member_id
, COALESCE(MAX(CASE WHEN type = 'A' THEN score END),0) a_score
, COALESCE(MAX(CASE WHEN type = 'B' THEN score END),0) b_score
, COALESCE(MAX(CASE WHEN type = 'A' THEN score END),0)
- COALESCE(MAX(CASE WHEN type = 'B' THEN score END),0) diff
FROM test2
GROUP
BY team_id
, member_id;

Insert new row for binary systems with SQL

I have the following table
(cl1 , cl2)
---- ----
(a , 1)
(a , 2)
(b , 2)
(c , 1)
(c , 2)
each a , b ,c can take two values (1 or 2 or both).
My question is :
How to insert a new row (with 0 on cl2) for all the cl1 that have only 1 or 2 and NOT the both in the example. I would like to insert the following row :
----
(b , 0)
----
I'm sure there are better ways, but here is one way to do it using group by and a having clause to enforce your rules (I'm assuming Oracle syntax):
insert into tbl (cl1, cl2)
(select cl1, 0
from tbl
group by cl1
having count(case when cl2 in (1, 2) then 'X' end) != 0 -- contains 1 or 2
and (count(case when cl2 = 1 then 'X' end) = 0 -- but not both
or count(case when cl2 = 2 then 'X' end) = 0)
)
EDIT
A much simpler way:
insert into tbl (cl1, cl2)
(select cl1, 0
from tbl
where cl2 in (1, 2)
group by cl1
having count(distinct cl2) = 1
)
I am assuming that the BD is Oracle. Hope the below snippet helps.
SELECT B.CL1,
0
FROM
(SELECT A.CL1,
CASE
WHEN WMSYS.WM_CONCAT(A.CL2) LIKE '%1%'
AND WMSYS.WM_CONCAT(A.CL2) LIKE '%2%'
THEN 'both'
ELSE 'one'
END rnk
FROM
(SELECT 'a' cl1,1 cl2 FROM dual
UNION ALL
SELECT 'a' cl1,2 cl2 FROM dual
UNION ALL
SELECT 'b' cl1,2 cl2 FROM dual
UNION ALL
SELECT 'c' cl1,1 cl2 FROM dual
UNION ALL
SELECT 'c' cl1,2 cl2 FROM dual
)A
GROUP BY A.CL1
)B
WHERE B.rnk = 'one';
CREATE TABLE TestTable (cl1 VARCHAR(2), cl2 INT);
INSERT INTO TestTable (cl1, cl2) VALUES ('a', 1), ('a', 2), ('b', 1), ('c', 1), ('c', 2);
INSERT INTO TestTable (cl1, cl2)
SELECT cl1, 0
FROM TestTable
WHERE cl1 NOT IN (
SELECT cl1
FROM TestTable
WHERE cl2 IN (1, 2)
GROUP BY cl1
HAVING COUNT(DISTINCT cl2) = 2
);
MySQL Demo: http://rextester.com/XWHGF50183
The below block returns the cl1 those have the cl2 is 1 and 2. Based on the result using NOT IN you can achieve the result.
SELECT cl1
FROM TestTable
WHERE cl2 IN (1, 2)
GROUP BY cl1
HAVING COUNT(DISTINCT cl2) = 2
Help from this answer
Here you go:
insert into [YOUR TABLE NAME]
select cl1,0 from [YOUR TABLE NAME]
group by cl1 having count(distinct cl2)<> 2
;

Mysql combinations of multiple coloumns

I have my table in this format
item A B C D
i1 4 0 2 0
i2 0 2 1 0
i3 2 0 0 2
i4 3 0 1 1
And, I'm looking for output where two columns are taken in combinations and if both elements value is >0 output value is taken as 1. And total count is calculated from all records
w1 w2 out
A B 0
A C 2
A D 2
B C 1
B D 0
C D 1
i,e for columns (A,C)>0 only i1 and i4 satisfy.So out=2
So far, I have solved this by querying for each item and then summing the value in php. Can this be possible entirely by sql query?
You can do it in SQL, but I think you still have to consider all the different combinations. Here is one approach using union all and conditional aggregation:
select col1name, col2name,
sum(col1 > 0 and col2 > 0)
from (select 'A' as col1name, 'B' as col2name, A as col1, B as col2
from t
union all
select 'A', 'C', A, C
from t
union all
select 'A', 'D', A, D
from t
union all
select 'B', 'C', B, C
from t
union all
select 'B', 'D', B, D
from t
union all
select 'C', 'D', C, D
from t
) t
EDIT:
There is another way, if you unpivot the data. This starts with this query:
select item, n.colname,
(case when n.colname = 'A' then A
when n.colname = 'B' then B
when n.colname = 'C' then C
else D
end) as colval
from t cross join
(select ';A' as colname union all select 'B' union all select 'C' union all select 'D'
);
We can now do a self join on the query to get all combinations and aggregate to get the results:
select col1.colname, col2.colname,
sum(col1.colval > 0 and col2.colval > 0)
from (select item, n.colname,
(case when n.colname = 'A' then A
when n.colname = 'B' then B
when n.colname = 'C' then C
else D
end) as colval
from t cross join
(select ';A' as colname union all select 'B' union all select 'C' union all select 'D'
)
) col1 join
(select item, n.colname,
(case when n.colname = 'A' then A
when n.colname = 'B' then B
when n.colname = 'C' then C
else D
end) as colval
from t cross join
(select ';A' as colname union all select 'B' union all select 'C' union all select 'D'
)
) col2
on col1.item = col2.item and
col1.colname < col2.colname
group by col1.colname, col2.colname;
This version is simpler if you have more than four columns. The number of combinations in the first methods will quickly become cumbersome.