Create view to get values from multiple tables - mysql

I thought it is easier to edit this post and give exact example what I meant by that post after all.
Main idea is that I need to get values from different tables.
Basically main idea is to select all values from Table B and Table C, but
selecting only values from Table C which are not present in Table B (but at the same time I need to
left join Table C to get text column value).
Table B and Table C has similar structure. They both have ref_num (ID) and text value.
Also Table B holds Table C ref_num, because when Table C entity is modified ("modifiable_column"),
then record is saved into Table B, but "default" value text column is taken from Table C.
It's something like.
Let's say we have default rules (Table C - always same values forever), then we have custom rules (Table B). Table D holds version of each rule with current as end_date IS NULL. Default values have default "modifiable_column" as 'N' as mentioned before. Now, let's say I take one rule from Table C and change "modifiable_column" to 'Y'. Then new row is created into Table B (with ref_num, table_c_ref_num, text = NULL). It means that this rule is now custom for this particular TabelA ref_num, at the same time new row is inserted into Table D (holding new row ref_num as table_b_ref_num and new "modifiable_column" value).
Now, when I want to select custom rules, default rules and versions (end_date IS NULL). I have to join Table B, Table C and Table D. But as Table C has always same rules, I only need to join it to get the text value. And I have to make sure I won't select duplicates. Meaning if Table C has 10 default rules, now one was modified and custom rules (Table B) has 1 rule. Then I have to so said select 1 from Table B and 9 from Table C, but at the same time I need to join Table C text value for this custom rule.
I have following tables as below:
create table TableA (
ref_num INT
);
create table TableB (
ref_num INT,
text VARCHAR(100),
table_c_ref_num INT,
table_a_ref_num INT
);
create table TableC (
ref_num INT,
text VARCHAR(100)
);
create table TableD (
ref_num INT,
table_b_ref_num INT,
modfifable_column VARCHAR(1),
start_date date,
end_date date
);
Inserting initial values as below:
insert into TableA (ref_num) values (1);
insert into TableC (ref_num, text) values
(1, "Text 1"),
(2, "Text 2"),
(3, "Text 3");
insert into TableB (ref_num, text, table_c_ref_num, table_a_ref_num) values
(1, NULL, 2, 1);
insert into TableD (ref_num, table_b_ref_num, modfifable_column, start_date, end_date) values
(1, 1, 'Y', now(), NULL);
Now I have created this select statement to get wanted behaviour:
SELECT * FROM (
SELECT
tb.ref_num AS ref_num,
tb.table_a_ref_num AS table_a_ref_num,
coalesce(tc.text, tb.text),
coalesce(tc.ref_num, tb.table_c_ref_num) AS table_c_ref_num,
coalesce(td.modfifable_column, 'N') AS modfifable_column
FROM TableB tb
LEFT JOIN TableD td on td.table_b_ref_num = tb.ref_num AND td.end_date IS NULL
LEFT JOIN TableC tc on tc.ref_num = tb.table_c_ref_num
WHERE tb.table_a_ref_num = 1
) as cust
UNION ALL
SELECT * FROM (
SELECT
NULL AS ref_num,
NULL AS table_a_ref_num,
tc2.text AS text,
tc2.ref_num AS table_c_ref_num,
'N' AS modfifable_column
FROM TableC tc2
WHERE tc2.ref_num NOT IN (
SELECT
tb2.table_c_ref_num
FROM TableB tb2
LEFT JOIN TableD td on td.table_b_ref_num = tb2.ref_num AND td.end_date IS NULL
LEFT JOIN TableC tc on tc.ref_num = tb2.table_c_ref_num
WHERE tb2.table_a_ref_num = 1
)
) as def;
I know that I can turn those two inner SELECT statements into views and then combine them with UNION ALL for example. My biggest concern here is that I have to "hard code" table_a_ref_num = 1 into two different places.
Because I have to use TableA ref_num in order to get custom values from TableB and default values from TableC. Because at the end TableA ref_num is like “this specific” entity custom rules and default rules.
My question is: Is there a way to wrap my big SELECT clause into one view, where I could use this TableA ref_num value to get results as in my example.

I don't fully understand your tables and pseudo code, but based on this limited understanding, my suggestion would be that you start with a query similar to this on:
select 'ta',ta.*,'tb',tb.*,'tc_via_b',tc_via_b.*,'tc_via_a',tc_via_a.*,'td',td.*
from table_a ta
left join table_b tb on tb.table_a_ref_num = ta.ref_num
left join table_c tc_via_b on tc_via_b.ref_num = tb.table_c_ref_num
left join table_c tc_via_a on tc_via_a.ref_num = ta.ref_num
left join table_d td on td.table_b_ref_num = tb.ref_num AND td.end_date IS NULL;
This way you will see all lines you want as a first step. In a second step, you should be able to use NVL and CASE to get the data you want. HTH

Related

SSRS report with many-to-many tables in sub-tablix

I have a main table (Table A). It has separate many-to-many relationships to two other tables (Table B and Table C).
I would like to create an SSRS report that has one row per record in Table A, but then displays a sub-table for each Table B and Table C in one of the columns.
I believe that I can do this by creating a sub-report for Table B and Table C, by passing the Table A ID to each of the two sub-reports. However, I'm wondering if this is possible to do without creating sub-reports.
Below is an example of the data structure and what I would like the report to look like.
This is a little long winded as I didn't have any sample data to work with. I created come sample data as follows.
CREATE TABLE TableA(aID int, a1 varchar(20), a2 varchar(20))
GO
CREATE TABLE TableB(bID int, b1 varchar(20), b2 varchar(20))
GO
CREATE TABLE TableC(cID int, c1 varchar(20), c2 varchar(20))
GO
INSERT INTO TableA VALUES (1,'Red', 'Green'), (2,'Blue', 'Purple')
INSERT INTO TableB VALUES (1,'Dave', 'Bob'), (1,'Geoff', 'Harry'), (2,'Jane', 'Mary'), (2,'Anne', 'Sue')
INSERT INTO TableC VALUES (1,'Dog', 'Cat'), (2,'Goat', 'Cow'), (2,'Sheep', 'Lamb'), (2,'Donkey', 'Horse'), (2,'Lizard', 'Frog')
I then created a query that joins the tables together, it includes a 'TableName' column to indicate which table it came from, we will use this to arrange the columns in a matrix. It also contains a row number for each row per table. The query looks like this.
SELECT
*
, RowN = ROW_NUMBER() OVER(PARTITION BY aID, TableName ORDER BY ID, FirstColumn, SecondColumn)
FROM (
SELECT
a.*
, 'Table B' as TableName
, bID as ID, b1 as FirstColumn, b2 as SecondColumn
FROM TableA a
JOIN TableB b on a.aID = b.bID
UNION ALL
SELECT
a.*
, 'Table C' as TableName
, c.*
FROM TableA a
JOIN TableC c on a.aID = c.cID
) u
In the port designer, I added a matrix control. I set the row groups to group by aID and Rown and added a column group by TableName .
Finally I set the first three columns Hide duplicates property to DataSet1
The report design looks like this..
The final output looks like this...
It's not perfect but it should be enough to get you going.

MySQL using an anti-join to select non duplicate values between two tables

I am trying to select values from table A based on values from table B. But, I can't figure out what to use.
Goal:
A user on my website gets a whole list of check-boxes from table A on a web-page.
Then the user chooses a check-box and the value from the check-box is
inserted into table B.
At some point in time, the user returns to that web-page and only sees the checkboxes that weren't inserted into table B.
In database terms, I would use a select query that compares table A (which holds ALL of the values) and table B (which basically stores a copy of a value from table A).
Here's my query. wp_ml_skill_class is table A and wp_ml_character_skill is table B
SELECT DISTINCT
s.skill_name, s.skill_id, c.char_id, c.um_id, c.class_id
FROM
`wp_ml_skill_class` sc
JOIN
`wp_ml_skill` s
ON
(s.skill_id = sc.skill_id)
JOIN
`wp_ml_character` c
WHERE
c.class_id = 3
AND
c.char_id = 5
AND
sc.skill_id
NOT IN
(SELECT cs.skill_id FROM wp_ml_character_skill cs);
You could do this with a LEFT JOIN between table A & B, checking for NULL results from table B, which will represent the rows in table A which are not in table B.
SELECT A.*
FROM wp_ml_skill_class A
LEFT JOIN wp_ml_character_skill B
ON B.skill_id = A.skill_id
WHERE B.skill_id IS NULL
Here's a small example to demonstrate:
create table A (id int, val varchar(10));
create table B (id int, val varchar(10));
insert into A values (1, 'a'), (2, 'b'), (3, 'c');
insert into B values (2, 'b');
SELECT *
FROM A
LEFT JOIN B
ON B.id = A.id
WHERE B.id IS NULL
Output:
id val id val
1 a (null) (null)
3 c (null) (null)

Find values missing in a column from a set (mysql)

I am using mysql.
I have a table that has a column id.
Let us say I have an input set of ids. I want to know which all ids are missing in the table.
If the set is "ida", "idb", "idc" and the table only contains "idb", then the returned value should be "ida", "idc".
Is this possible with a single sql query? If not, what is the most efficient way to execute this.
Note that I am not allowed to use stored procedure.
MySQL will only return rows that exist. To return missing rows you must have two tables.
The first table can be temporary (session/connection specific) so that multiple instances can run simultaneously.
create temporary table tmpMustExist (text id);
insert into tmpMustExist select "ida";
insert into tmpMustExist select "idb";
-- etc
select a.id from tmpMustExist as a
left join table b on b.id=a.id
where b.id is null; -- returns results from a table that are missing from b table.
Is this possible with a single sql query?
Well, yes it is. Let me work my way to that, first with a union all to combine the select statements.
create temporary table tmpMustExist (text id);
insert into tmpMustExist select "ida" union all select "idb" union all select "etc...";
select a.id from tmpMustExist as a left join table as b on b.id=a.id where b.id is null;
Note that I use union all which is a bit faster than union because it skips over deduplication.
You can use create table...select. I do this frequently and really like it. (It is a great way to copy a table as well, but it will drop indexes.)
create temporary table tmpMustExist as select "ida" union all select "idb" union all select "etc...";
select a.id from tmpMustExist as a left join table as b on b.id=a.id where b.id is null;
And finally you can use what's called a "derived" table to bring the whole thing into a single, portable select statement.
select a.id from (select "ida" union all select "idb" union all select "etc...") as a left join table as b on b.id=a.id where b.id is null;
Note: the as keyword is optional, but clarifies what I'm doing with a and b. I'm simply creating short names to be used in the join and select field lists
There's a trick. You can either create a table with expected values or you can use union of multiple select for each value.
Then you need to find all the values that are in the etalon, but not in the tested table.
CREATE TABLE IF NOT EXISTS `single` (
`id` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `single` (`id`) VALUES
('idb');
SELECT a.id FROM (
SELECT 'ida' as id
UNION
SELECT 'idb' as id
UNION
SELECT 'idc' AS id
) a WHERE a.id NOT IN (SELECT id FROM single)
//you can pass each set string to query
//pro-grammatically you can put quoted string
//columns must be utf8 collation
select * from
(SELECT 'ida' as col
union
SELECT 'idb' as col
union
SELECT 'idc' as col ) as setresult where col not in (SELECT value FROM `tbl`)

One Row to Match Them All

So I have a situation where I have two tables. One is the base table (called _Keys in this example), with a unique primary key. Then there is another table with multiple rows of a data for each id in _Keys (this second table is Extra).
I need to select the largest value for each primary key in _Keys from Extra. I have made an SQLFiddle to model the problem here.
This is the query I'm currently using, but the issue is that it will only select one value for the Extra table, not one value per row.
Select * from _Keys
LEFT JOIN
(Select * from Extra ORDER BY value2 DESC LIMIT 1) as e
ON e.id = _Keys.id;
For my example SQL Fiddle I used this database schema:
CREATE TABLE _Keys(id int, value int);
INSERT INTO _Keys (id, value) VALUES (1, 5),(2, 3),(3, 4);
CREATE TABLE Extra(id int, value2 int);
INSERT INTO Extra (id, value2) VALUES (1, 3),(1, 1),(2, 4),(2, 6),(3, 3),(3, 5);
Basically my result is here. Only the first row from the _Keys table gets its data from the second table.
In MySQL, how can I achieve selecting one row from Extras for each row in _Keys?
I believe I understand what you are trying to do but I'm not sure.
You are getting NULL values because of the LIMIT, it only returns the first row. You also need to use GROUP BY.
To get the largest value, your can use MAX.
Try this.
SELECT * from _Keys
LEFT JOIN
(SELECT id, MAX(value2) AS value2 FROM Extra GROUP BY id) as e
ON e.id = _Keys.id;
Your joined table Select * from Extra ORDER BY value2 DESC LIMIT 1 will contain only one row because of LIMIT. Try this:
Select * from _Keys
LEFT JOIN
(Select id, max(value2) from Extra group by id) as e
ON e.id = _Keys.id;
You can try this query with better performance :
SELECT k.id, MAX(e.value2) AS value2
FROM _Keys k
INNER JOIN Extra e
ON (k.id = e.id)
GROUP BY k.id;

Returning most recent row in table from a join

I have two tables. Consider them Table A and Table B.
Table A has a list of things in it.
Table B might have entries which point back to the things in Table A.
That is to say, for every row in Table A, there may be 0 rows or n rows in Table B that link back to it (via some ID column also in Table B).
What I want to do is run 1 SELECT statement with some kind of JOIN which returns a list of all the rows from Table A (all columns) and 1 column from Table B where that 1 column from Table B is ordered DESC (the column is a timestamp, and I want the most recent timestamp).
That is to say, I want my SELECT statement to return 1 row for each row in Table A. One of the columns in the result set will be data from Table B, and it has to grab the data from the most recent entry corresponding to the Table A row.
I'm really at a loss on how to do this. I've even tried adding "TableB.created_on = max(TableB.created_on)" to the ON clause of the JOIN.
Assuming sample schema looks like
CREATE TABLE a
(id INT, description VARCHAR(64));
CREATE TABLE b
(id INT, timestamp timestamp);
You can do
SELECT *,
(SELECT MAX(timestamp)
FROM b
WHERE id = a.id) max_timestamp
FROM a
or
SELECT a.id, a.description, MAX(b.timestamp)
FROM a JOIN b
ON a.id = b.id
GROUP BY a.id, a.description
SQLFiddle (for both queries)