Insert new row for binary systems with SQL - mysql

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
;

Related

Mysql query to decide the type if group by value is greater than 1 consider as type 2 else 1

I want to write a case condition in mysql query when grouped by channelvalue if the count is more than 1 it is considered as type_id 3 if there is no duplicates for the channelvalue then the type_id should be 2
else 0
select b.ChannelValue
case
when count(*),ChannelValue from tableb group by ChannelValue having count(*)=1 then 2
when count(*),ChannelValue from tableb group by ChannelValue having count(*)>1 then 3
else 0 END AS type_id
from tablea a inner join tableb b
on a.ChannelValue = b.ChannelValue;
Help me out !
You could use something like below:
select ChannelValue,
case
when (
select count(*) from tableb
group by tableb.ChannelValue
having tableb.ChannelValue = tablea.ChannelValue) = 1 then 2
when(
select count(*) from tableb
group by tableb.ChannelValue
having tableb.ChannelValue = tablea.ChannelValue) > 1 then 3
else 0
END as type_id
from tablea;
Say if you have this test data:
INSERT INTO tablea (PersonID, ChannelValue)
VALUES (1, 100),
(2, 200),
(3, 300);
INSERT INTO tableb (testID, ChannelValue, characterid)
VALUES (1, 100, 1),
(2, 100,2),
(3, 300, 3);
The query would return:
ChannelValue
type_id
100
3
200
0
300
2

SQL query - credit , debit , balance

DISCLAIMER : I Know this has been asked numerous times, but all I want is an alternative.
The table is as below :
create table
Account
(Name varchar(20),
TType varchar(5),
Amount int);
insert into Account Values
('abc' ,'c', 500),
('abc', 'c', 700),
('abc', 'd', 100),
('abc', 'd', 200),
('ab' ,'c', 300),
('ab', 'c', 700),
('ab', 'd', 200),
('ab', 'd', 200);
Expected result is simple:
Name Balance
------ -----------
ab 600
abc 900
The query that worked is :
select Name, sum(case TType when 'c' then Amount
when 'd' then Amount * -1 end) as balance
from Account a1
group by Name.
All I want is, is there any query sans the 'case' statement (like subquery or self join ) for the same result?
Sure. You can use a second query with a where clause and a union all:
select name
, sum(Amount) balance
from Account a1
where TType when 'c'
group
by Name
union
all
select name
, sum(Amount * -1) balance
from Account a1
where TType when 'd'
group
by Name
Or this, using a join with an inline view:
select name
, sum(Amount * o.mult) balance
from Account a1
join ( select 'c' cd
, 1 mult
from dual
union all
select 'd'
, -1
from dual
) o
on o.cd = a1.TType
group
by Name
To be honest, I would suggest to use case...
Use the ASCII code of the char and try to go from there. It is 100 for 'd' and 99 for 'c'. Untested example:
select Name, sum((ASCII(TType) - 100) * Amount * (-1)) + sum((ASCII(TType) - 99) * Amount * (-1)))) as balance from Account a1 group by Name.
I would not recommend using this method but it is a way of achieving what you want.
select t.Name, sum(t.cr) - sum(t.dr) as balance from (select Name, case TType when 'c' then sum(Amount) else 0 end as cr, case TType when 'd' then sum(Amount) else 0 end as dr from Account group by Name, TType) t group by t.Name;
This will surely help you!!
The following worked for me on Microsoft SQL server. It has the Brought Forward balance as well
WITH tempDebitCredit AS (
Select 0 As Details_ID, null As Creation_Date, null As Reference_ID, 'Brought
Forward' As Transaction_Kind, null As Amount_Debit, null As Amount_Credit,
isNull(Sum(Amount_Debit - Amount_Credit), 0) 'diff'
From _YourTable_Name
where Account_ID = #Account_ID
And Creation_Date < #Query_Start_Date
Union All
SELECT a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit, a.Amount_Debit - a.Amount_Credit 'diff'
FROM _YourTable_Name a
where Account_ID = #Account_ID
And Creation_Date >= #Query_Start_Date And Creation_Date <= #Query_End_Date
)
SELECT a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit, SUM(b.diff) 'Balance'
FROM tempDebitCredit a, tempDebitCredit b
WHERE b.Details_ID <= a.Details_ID
GROUP BY a.Details_ID, a.Creation_Date, a.Reference_ID, a.Transaction_Kind,
a.Amount_Debit, a.Amount_Credit
Order By a.Details_ID Desc

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.

MySQL matrix multiplication

I am trying to write matrix multiplication for MySQL and am kinda stuck:
basically, my matrices are stored in format
[row#, column#, matrixID, value], so for example matrix [3 x 2] would be something like:
[row#, column#, matrixID, value]
1 1 mat01 1
1 2 mat01 2
1 3 mat01 3
2 1 mat01 4
2 2 mat01 5
2 3 mat01 6
being equivalent to: [[1 2 3],[4 5 6]]
following does calculation of single element of matrix1 * matrix2 quite well:
SELECT SUM(row1.`val` * col2.`val`)
FROM matValues row1
INNER JOIN `matValues` col2
WHERE row1.`row` = 1 AND row1.`mID`='matrix1' AND
col2.`mID`='matrix2' AND col2.`col` = 1 AND row1.col = col2.row
wrapping this into function and then using another function to iterate over row and column numbers might work, but I have problems with generating this set of numbers and iterating over them using SQL.
Any advice / suggestions are welcome
Try:
select m1.`row#`, m2.`column#`, sum(m1.value*m2.value)
from matValues m1
join matValues m2 on m2.`row#` = m1.`column#`
where m1.matrixID = 'mat01' and m2.matrixID = 'mat02'
group by m1.`row#`, m2.`column#`
Example here.
(Replace 'mat01' and 'mat02' with suitable matrixID values.)
You can do the entire calculation in SQL. You only give an example with a single matrix, which because it is not square, cannot be multiplied by itself.
Here is the idea:
SELECT mout.row, mout.col, SUM(m1.value*m2.value)
FROM (select distinct row from matValues cross join
select distinct COL from matValues
) mout left outer join
matValues m1
on m1.row = mout.row left outer join
matValues m2
on m2.col = mout.col and
m2.row = m1.col
I know this is SQL-Server syntax, but it should give you a start on the corresponding MySql syntax. The sparse matrix nature seems to handle well.
with I as (
select * from ( values
(1,1, 1),
(2,2, 1),
(3,3, 1)
) data(row,col,value)
)
,z_90 as (
select * from ( values
(1,2, 1),
(2,1,-1),
(3,3, 1)
) data(row,col,value)
)
,xy as (
select * from ( values
(1,2, 1),
(2,1, 1),
(3,3, 1)
) data(row,col,value)
)
,x_90 as (
select * from ( values
(1,1, 1),
(2,3, 1),
(3,2,-1)
) data(row,col,value)
)
select
'I * z_90' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from I as a
join z_90 as b on a.col = b.row
group by a.row, b.col
union all
select
'z_90 * xy' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from z_90 as a
join xy as b on a.col = b.row
group by a.row, b.col
union all
select
'z_90 * x_90' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from z_90 as a
join x_90 as b on a.col = b.row
group by a.row, b.col
order by instance, a.row, b.col
yields:
instance row col value
----------- ----------- ----------- -----------
I * z_90 1 2 1
I * z_90 2 1 -1
I * z_90 3 3 1
z_90 * x_90 1 3 1
z_90 * x_90 2 1 -1
z_90 * x_90 3 2 -1
z_90 * xy 1 1 1
z_90 * xy 2 2 -1
z_90 * xy 3 3 1
However, I suggest you also check out performing this on your graphics card. NVIDIA has a good example of implementing matrix multiplication in theri C Programming Guide.

Sql Server 2008 Select From table with AND style conditions in related tables

Given a model like this
ProductFacets contains the following data:
ProductId, FacetTypeId
1, 1
1, 2
2, 1
2, 3
3, 4
3, 5
4, 1
4, 2
I'd like to be able to select all Products which have a FacetTypeId of 1 AND 2.
The result set should contain ProductIds 1 and 4
This will return rows for products that have only facet types 1 and 2, and only those facets.
SELECT ProductId,
COUNT(*) AS FacetCountByProduct,
SUM(CASE WHEN FacetTypeId in (1, 2) THEN 1 ELSE 0 END) AS FacetCountSelectedFacets
FROM ProductFacets
GROUP BY ProductId
HAVING COUNT(*) = 2
and SUM(CASE WHEN FacetTypeId in (1, 2) THEN 1 ELSE 0 END) = 2
;
SELECT * FROM Product PROD WHERE PROD.ProductId IN(
SELECT P.ProductId pId FROM ProductFacets AS P
WHERE P.FacetTypeId = 1
AND EXISTS
(
SELECT *
FROM ProductFacets AS P1
WHERE P1.FacetTypeid = 2
AND P1.ProductId = pId
)
AND NOT EXISTS
(
SELECT *
FROM ProductFacets AS P2
WHERE P2.FacetTypeid NOT IN (1,2)
AND P2.ProductId = pId
)
)
There must be a better way to solve this, but it's the only one i can come up with
Just thought of a way to do this:
select distinct ProductId from ProductFacets
where ProductId in (select ProductId from ProductFacets where FacetTypeId = 1)
and ProductId in (select ProductId from ProductFacets where FacetTypeId = 2)