Mysql Dynamic crosstab Pivot comparison on one table - mysql

I have a many to many table setup. This is an example of table I am focusing on.
many_to_many_id, foreign_key_id
1, 1
1, 2
1, 3
2, 1
2, 2
3, 1
3, 4
I need to given many_to_many_id 1 find any other many_to_many_ids that have matching foreign keys that exist within the first set. Given the first set 1, 2, 3 attached to many_to_many_id would return 2 as 1, and 2 are inside the set, but 3 would not be returned as 4 is not part of the test set. My boss has said I should use a dynamic cross tab to create two tables to compare with a join. I have looked for examples but they have not been helpful.

You can do this with a few simple sub-queries. The first will make sure that for each many_to_many_id there is at least one foreign_key_id in the set you're looking for (1, 2, 3) and the second will make sure there isn't even one foreign_key_id not in the set you're looking for.
SET #search_id = 1;
SELECT m.many_to_many_id FROM SampleTable m
WHERE m.many_to_many_id != #search_id
AND EXISTS ( SELECT 1 FROM SampleTable a WHERE a.many_to_many_id = m.many_to_many_id AND a.foreign_key_id IN ( SELECT b.foreign_key_id FROM SampleTable b WHERE b.many_to_many_id = #search_id ) )
AND NOT EXISTS ( SELECT 1 FROM SampleTable a WHERE a.many_to_many_id = m.many_to_many_id AND a.foreign_key_id NOT IN ( SELECT b.foreign_key_id FROM SampleTable b WHERE b.many_to_many_id = #search_id ) )
GROUP BY m.many_to_many_id
SQL Fiddle Here

Related

MySQL WHERE in IF Statement of View

I'm trying to create a view of 3 tables. There is one line that is messing up everything and I can't get it right. Its in the "IF" statement below. I know I can't use WHERE inside an if statement, but that is what I'm trying to achieve.
Table 'c' (The qms_dpus table) is a table that stores ERRORS for each job. What I'm trying to do is say
"for every job that is inside table 'a', check table 'c' to see if the JOBNUM columns match AND the IS_COMPLETE column is 0. If that is true then put a 1 in this new Column OPEN_DPUS. If it is false, then put a 0 in the column"
CREATE OR REPLACE VIEW `view_qms_linea` AS SELECT
a.ID AS `ID`,
a.JID AS `JID`,
b.HPL AS `HPL`,
b.LINE AS `LINE`,
a.PAREA AS `PRODAREA`,
a.JOBNUM AS `JOBNUM`,
b.MODELNUMBER AS `MODELNUMBER`,
b.CUSTOMER AS `CUSTOMER`,
b.PM_AE AS `PM_AE`,
b.PM_PE AS `PM_PE`,
b.PM_DE AS `PM_DE`,
b.PM_EE AS `PM_EE`,
b.PM_ED AS `PM_ED`,
b.PM_CE AS `PM_CE`,
IF(c.IS_COMPLETE=0 WHERE c.JOBNUM LIKE a.JOBNUM, 1, 0) AS `OPEN_DPUS`
FROM `gen_jobs_in_production` a, `gen_jobs_table` b, `qms_dpus` c
WHERE
a.JOBNUM LIKE b.WO
AND
b.LINE = 1
AND
a.PAREA != 4
AND
a.PAREA <= 7;
When i remove the IF statement (and the reference to table 'c') the code works great and provides me with what I need aside from the column OPEN_DPUS, but if I replace the IF statement with this:
IF(c.JOBNUM LIKE a.JOBNUM AND c.IS_COMPLETE=0,1,0) AS `OPEN_DPUS`
My view provides me with all of the data, but instead of being 12 records (currently), It repeats every record 12 times and repeats that pattern infinitely.
I would love to display my table structure, but I have many columns and would only make this more confusing...
Any help would be greatly appreciated!
Your query was missing join conditions. Hence it was returning you the cross joined results of the 3 tables in your query. Try the query below.
CREATE OR REPLACE VIEW `view_qms_linea` AS SELECT
a.ID AS `ID`,
a.JID AS `JID`,
b.HPL AS `HPL`,
b.LINE AS `LINE`,
a.PAREA AS `PRODAREA`,
a.JOBNUM AS `JOBNUM`,
b.MODELNUMBER AS `MODELNUMBER`,
b.CUSTOMER AS `CUSTOMER`,
b.PM_AE AS `PM_AE`,
b.PM_PE AS `PM_PE`,
b.PM_DE AS `PM_DE`,
b.PM_EE AS `PM_EE`,
b.PM_ED AS `PM_ED`,
b.PM_CE AS `PM_CE`,
--change from here
IF(c.IS_COMPLETE = 0, 1, 0) AS `OPEN_DPUS`
FROM `gen_jobs_in_production` a
JOIN `gen_jobs_table` b ON a.JOBNUM = b.WO
JOIN `qms_dpus` c ON c.jobnum = a.jobnum
WHERE b.LINE = 1
AND a.PAREA != 4
AND a.PAREA <= 7;

Select multiple rows with multiple matching id(s)

Is there a way to query data to display multiple rows with multiple selector?
like:
SELECT * FROM `book` WHERE `book`.id = 1 AND `book`.id = 2;
table:
id name
1 book1
2 book2
3 book3
4 book4
I dun suppose looping for each id is favourable.
Well, yeah, you can use in, but this is the same as multiple or, not and – one value can't be equal to the two unequal values at once:
select *
from `book`
where `book`.id in(1, 2)
One solution you have, but you made an error. An id cannot be both 1 and 2, so you need to use OR to return those rows that are either 1 or 2.
SELECT * FROM `book` WHERE `book`.id = 1 OR `book`.id = 2;
A slightly simpler notation would be:
SELECT * FROM `book` WHERE `book`.id in (1, 2);

SQL query one table multiple choices

I need help for designing an SQL statement. It will be created on the fly in a Web Server back end, depending on the request. This will filter a database for the desired results.
To describe my need in simplest form:
One table, two fields, 50 staff, 30 skills.
Example:
staff_id, skill_id
1, 1
1, 2
1, 3
1, 4
2, 2
2, 3
2, 4
3, 2
4, 3
Q: Who has Skill 3
A: Staff 1,2,4
(This is easy)
Q: Who has Skill 2 AND 3
A: Staff 1,2
The actual query will have a request for 5 or 6 Skills
Considering that the number of skills varies from time to time, the best way to handle this via temp table..Here is the pseudocode...syntax needs to be fixed.
create procedure my_procedure(string of skillsets)
begin
create table #tmp_skillsets(skillset smallint)
foreach skillset in skillsets
insert into #tmp_skillsets(convert_to_smallint(skillset))
select distinct staff from myTable, #tmp_skillset
where myTable.skillset = #tmp_skillsets
end
Call this procedure like: "exec my_procedure("1456") or "exec my_procedure("179248503")
I think this should work:
SELECT DISTINCT staff_id
FROM oneTable t
WHERE EXISTS (SELECT 1 FROM oneTable ti WHERE t.staff_id = ti.staff_id AND ti.skill_id = 2)
AND EXISTS (SELECT 1 FROM oneTable ti WHERE t.staff_id = ti.staff_id AND ti.skill_id = 3)

SQL, build a query using data provided in the query itself

For experimental purposes only.
I would like to build a query but not querying data extracted for any table but querying data provided in the query it self. Like:
select numbers.* from (1, 2, 3) as numbers;
or
select numbers.* from (field1 = 1, field2 = 2, field3 = 3) as numbers;
so I can do things like
select
numbers.*
from (field1 = 1, field2 = 2, field3 = 3) as numbers
where numbers.field1 > 1;
If the solution is specific for a database engine could be interesting too.
If you wanted the values to be on separate rows instead of three fields of the same row, the method is the same, just one row per value linked with a union all.
select *
from(
select 1 as FieldName union all
select 2 union all
select 3 union all
select 4 union all -- we could continue this for a long time
select 5 -- the end
) as x;
select numbers.*
from(
select 1 ,2, 3
union select 3, 4, 5
union select 6, 7, 8
union select 9, 10, 11 -- we could continue this for a long time
union select 12, 13, 14 -- the end
) as numbers;
This works with MySQL and Postgres (and most others as well).
[Edit] Use union all rather than just union as you do not need to remove duplicates from a list of constants. Give the field(s) in the first select a meaningful name. Otherwise, you can't specify a specific field later on: where x.FieldName = 3.
If you don't provide meaningful names for the fields (as in the second example), the system (at least MySQL where this was tested) will assign the name "1" for the first field, "2" as the second and so on. So, if you want to specify one of the fields, you have to write expressions like this:
where numbers.1 = 3
Use the values row constructor:
select *
from (values (1),(2),(3)) as numbers(nr);
or using a CTE.
with numbers (nr) as (
values (1),(2),(3)
)
select *
from numbers
where nr > 2;
Edit: I just noticed that you also taggeg your question with mysql: the above will not work with MySQL, only with Postgres (and a few other DBMS)
You can use a subquery without table like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
UNION
SELECT
4,
5,
6
) AS numbers
WHERE
numbers.a > 1
If you like queries to always have a table referenced there is a Psuedo table that always has 1 row and no columns called DUAL, you can use it like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
FROM
DUAL
UNION
SELECT
4,
5,
6
FROM
DUAL
) AS numbers
WHERE
numbers.a > 1

MYSQL query to Update the field if found duplicates?

Here is my problem. I got a table Meaning
ID - Meaning
1 - red car
2 - cat man
3 - red car
4 - ontime
5 - red car
....
I want to make the colum Meaning become Unique. So i want to build a query to found all the duplicates & for each of duplicate, the system should append [number] to make the cell become unique.
So after running that query, the result should be:
ID - Meaning
1 - red car
2 - cat man
3 - red car [2]
4 - ontime
5 - red car [3]
....
The table is pretty long about 100K rows. The query could be similar to this query
Update Table Meaning set meaning=concat(meaning,"1")
where meaning in (select meaning from Meaning group by meaning having count(meaning>1)
So what is the query for solving the problem?
Seem we have to use set variable to check each row?
step 1: create temporary table
CREATE TABLE TMP (id int, meaning varchar (2));
step 2: prepare query and insert into temporary table
insert into tmp
SELECT id,
CASE WHEN cnt =0 theN meaning ELSE concat(meaning,'[',cnt+1,']') END AS meaning
FROM
(
SELECT t1.id, t1.meaning, (
SELECT COUNT( t.id )
FROM test t
where t.meaning=t1.meaning
and t.id<t1.id
) as cnt
FROM test t1
)TMP
step 3
truncate table test
step 4: migrate to original
insert into test select * from tmp
SELECT x.*
, CONCAT(x.meaning,CASE WHEN COUNT(*) = 1 THEN '' ELSE COUNT(*) END) meaning
FROM meanings
x JOIN meanings
y ON y.meaning = x.meaning
AND y.id <= x.id
GROUP
BY id;