comparing two SQL tables set cut-set to true and else to false - mysql

for my new project I decided to optimize all kind of inefficient SQL queries I've used on my older projects before importing it. After some hours on a problem that in my opinion seems to be very simple I am at a loss. I've got two different Tables that are holding user-IDs
table r table s
+-----+ +-----+
| user| | user|
+-----+ +-----+
| 2 | | 1 |
| 3 | | 4 |
| 4 | +-----+
+-----+
what I want now is to compare both Tables and setting a true or false flag in another column of the result table which shows me what value of table s is in table r. So what I want is just set the cut-set to true. the result table should look like this
result
+-----+-------+
| user| flag |
+-----+-------+
| 1 | FALSE |
+-----+-------+
| 4 | TRUE |
+-----+-------+
So the user 4 is the only user that is listed in table r. I already tried some solutions with IF clauses but I'm not pro in SQL but I do my best to get closer to that aim. My idea was this query
SELECT
r.user,
IF(r.user = s.user, TRUE, FALSE) AS flag
FROM
r,
s
I know that it isn't correct at all because the query gives me the following 6 rows.
result
+-----+-------+
| user| flag |
+-----+-------+
| 2 | 0 |
+-----+-------+
| 2 | 0 |
+-----+-------+
| 3 | 0 |
+-----+-------+
| 3 | 0 |
+-----+-------+
| 4 | 0 |
+-----+-------+
| 4 | 1 |
+-----+-------+
so the last row to each user seems to be the result I want but the problem is that the false table is combined with the flag and each user has two flags. I don't understand whats going wrong and I would be appreciated if someone could explain me how the SQL server understands my query and what I have to change to get the correct result

You have no join condition, so your query is simply creating a cartesian product between the two tables. It then reports true or false depending on whether the users match in a particular pairing. You would see this if you included s.user in your SELECT clause.
To do what you want, you should use an outer join.
SELECT s.user, r.user IS NOT NULL AS flag
FROM s
LEFT JOIN r
ON r.user = s.user

Try:
SELECT r.user FROM r,s flag WHERE r.user = s.user

Related

Generate table with columns dynamically

Say we have a table named questions with the following schema and content
id | name | text
1 | Q1 | Java: some text
2 | Q2 | Python: other text
3 | Q3 | C#: something else
then a table called answers with something like this
id | uid | qid | value
1 | 100 | 1 | true
2 | 100 | 3 | false
3 | 101 | 2 | false
4 | 101 | 3 | true
where qif is a FK to the question table, and uid a key to some other user table (not important here)
Now I'd like to generate the following table:
uid | Java | Python | C#
100 | true | null | false
101 | null | false | true
that is, a table that for each user contains all different questions taken from its table, with its column name generated dynamically based on its text (ie. using substring for instance)
My initial approach is the naive one, using something like this:
SELECT uid, java
FROM answer
LEFT JOIN
(SELECT a.uid, q.value AS "java"
FROM answer a
INNER JOIN question q ON q.id = a.qid
WHERE q.id=1) java
ON java.uid = u.id
LEFT JOIN
// same for "python", "c#"...
...
that is, generating each column by appending to it through a join. This of course works fine when having few items, but I was wondering if there is any alternative approach to do it more generically, without having to do these repetitive LEFT JOINS
Any help is greatly appreciated!

Issue with grouping?

I asked earlier about a solution to my problem which worked however now when I'm trying to get some information from a second table (that stores more information) I'm running into a few issues.
My tables are as follows
Users
+----+----------------------+---------------+------------------+
| id | username | primary_group | secondary_groups |
+----+----------------------+---------------+------------------+
| 1 | Username1 | 3 | 7,10 |
| 2 | Username2 | 7 | 3,5,10 |
| 3 | LongUsername | 1 | 3,7 |
| 4 | Username3 | 1 | 3,10 |
| 5 | Username4 | 7 | |
| 6 | Username5 | 5 | 3,7,10 |
| 7 | Username6 | 2 | 7 |
| 8 | Username7 | 4 | |
+----+----------------------+---------------+------------------+
Profile
+----+---------------+------------------+
| id | facebook | steam |
+----+---------------+------------------+
| 1 | 10049424151 | 11 |
| 2 | 10051277183 | 55 |
| 3 | 10051281183 | 751 |
| 4 | | 735 |
| 5 | 10051215770 | 4444 |
| 6 | 10020210531 | 50415 |
| 7 | 10021056938 | 421501 |
| 8 | 10011547143 | 761 |
+----+---------------+------------------+
My SQL is as follows (based off the previous thread)
SELECT u.id, u.username, p.id, p.facebook, p.steam
FROM users u, profile p
WHERE p.id=u.id AND FIND_IN_SET( '7', secondary_groups )
OR primary_group = 7
GROUP BY u.id
The problem is my output is displayed as below
+----+----------------------+-------------+-------+
| id | username | facebook | steam |
+----+----------------------+-------------+-------+
| 1 | Username1 | 10049424151 | 11 |
| 2 | Username2 | 10051277183 | 55 |
| 3 | LongUsername | 10051281183 | 751 |
| 4 | Username4 | 10051215770 | 4444 |
| 5 | Username5 | 10049424151 | 11 |
| 6 | Username6 | 10049424151 | 55 |
+----+----------------------+-------------+-------+
I'm guessing that the problem is that profile rows with a primary_group of 7 are getting matched to all user rows. Remove the GROUP BY, and you'll be able to better see what is happening.
But that's just a guess. It's not clear what you are attempting to achieve.
I suspect you are getting tripped up with the order of precedence of the AND and OR. (The AND operator has a higher order of precedence than OR operator. That means the AND will be evaluated before the OR.)
The quick fix is to just add some parens, to override the default order of operations. Something like this:
WHERE p.id=u.id AND ( FIND_IN_SET('7',secondary_groups) OR primary_group = 7 )
-- ^ ^
The parens will cause the OR operation to be evaluated (as either TRUE, FALSE or NULL) and then the result from that will be evaluated in the AND.
Without the parens, it's the same as if the parens were here:
WHERE ( p.id=u.id AND FIND_IN_SET('7',secondary_groups) ) OR primary_group = 7
-- ^ ^
With the AND condition evaluated first, and the result from that is operated on by OR. This is what is causing profile rows with a 7 to be matched to rows in user with different id values.
A few pointers on style:
avoid the old-school comma operator for join operations, and use the newer JOIN syntax
place the join predicates (conditions) in the ON clause, other filtering criteria in the WHERE clause
qualify all column references
As an example:
SELECT u.id
, u.username
, p.id
, p.facebook
, p.steam
FROM users u
JOIN profile p
ON p.id = u.id
WHERE u.primary_group = 7
OR FIND_IN_SET('7',u.secondary_groups)
ORDER BY u.id
We only need a GROUP BY clause if we want to "collapse" rows. If the id column is unique in both the users and profile tables, then there's no need for a GROUP BY u.id. We can add an ORDER BY clause if we want rows returned in a particular sequence.
I don't know, what exactly do you want to do with output, but you can't group informations like this. MySQL isn't really a classic programming language, it's more like powerful tool for set mathematics. So if you want to get informations based on corelations between two or more tables, first you write a select statement which contains raw data which you want to work with, like this:
SELECT * FROM users u INNER JOIN profile p ON p.id=u.id
GROUP BY u.id;
Now you select relevant data with WHERE statement:
SELECT * FROM users u INNER JOIN profile p ON p.id=u.id WHERE
FIND_IN_SET( '7', secondary_groups ) OR primary_group = 7
GROUP BY u.id;
Now you should see grouped joined tables profile and users, and can start mining data. For example, if you want to count items in these groups, just add count function in SELECT and so on.
When debugging SQL, I highly recommend these steps:
1.) First, you should write down all corelations between data, all foreign keys between tables, so you will know if your selection is fully deterministic. You can now start JOINing tables from left to right
2.) Try small bits of querys on model database. Then you will see which selection works right and which doesn't do what you expected.
I think #SIDU has it in the comments: You are experiencing a Boolean order of operations problem. See also SQL Logic Operator Precedence: And and Or
For example:
SELECT 0 AND 0 OR 1 AS test;
+------+
| test |
+------+
| 1 |
+------+
When doing complex statements with both AND and OR, use parenthesis. The operator order problem is leading to you doing an unintended outer join that's being masked by your GROUP BY. You shouldn't need a GROUP BY for that statement.
Although I don't personally care for the style #spencer7593 suggests in his answer(using INNER JOIN, etc.), it does have the advantage of preventing or identifying errors early for people new to SQL, so it's something to consider.

MySQL In result are NULL's when join left 3 tables

I have problem with result when join 3 tables because I have in some place NULL a should be number or empty cell.
My tables in database:
Table nr 1: rysunek
id_rys | nazwa_rys | nazwa_klienta | ...
3 |01_116230_C0 |PHILIPS
7 |11_002177_A0 |P&G
20 |01_101854_B0 |MARS FOOD
333 |None |None
( + 7 columns which do not use in this query)
Table nr 2: artykul
id_art |id_rys |nazwa_art | id_status | ...
1 |3 |00_16_1234 | 1
2 |7 |00_16_1235 | 3
3 |7 |00_16_1236 | 0
4 |333 |00_16_1237 | 0
( + 10 columns which do not use in this query)
Table nr 3: statusy
id_status |kod_status
1 |IA
2 |NC
3 |861
Mysql query looks like this:
SELECT r.nazwa_klienta
, r.nazwa_rys
, a.nazwa_art
, s.kod_status
FROM artykul a
LEFT
JOIN rysunek r
ON a.id_rys = r.id_rys
LEFT
JOIN statusy s
ON a.id_status = s.id_status;
And result looks like this:
nazwa_klienta | nazwa_rys | nazwa_art | kod_status
NULL | NULL | 00_16_1234 | IA
NULL | NULL | 00_16_1235 | 861
P&G | 11_002177_A0 | 00_16_1236 | NULL
None | None | 00_16_1237 | NULL
I need to the result of query above look like this:
nazwa_klienta | nazwa_rys | nazwa_art | kod_status
PHILIPS | 01_116230_C0 | 00_16_1234 | IA
P&G | 11_002177_A0 | 00_16_1235 | 861
P&G | 11_002177_A0 | 00_16_1236 | [empty cell]
None | None | 00_16_1237 | [empty cell]
How should looks like my query? I tried all join methods but none of them work.
Maybe I should change structure of my tables? I'm waiting for some suggestion from somebody... :)
Left joins are going to create empty entries for your rysunek columns whenever there is no row in that table that matches artykul's. If you don't want them listed, then don't use an outer join between those two tables (I note you didn't list them in your desired output).
For the other NULLs, from kod_status, since it is a numeric column, pretty much your choices are: take the null; or turn it into a 0 with a COALESCE(kod_status,0), or cast the result to a string and turn its nulls into empty strings. There are command-line options in the mysql command-line tool (which your output appears to be from) (see http://dev.mysql.com/doc/refman/5.7/en/mysql-commands.html ) there may be a way to change how it outputs nulls if that would fit your use-model needs.

Update tables based on NULL values in a derived table

I want to start out with this as a basis for my question that I may already know the answer to.
mysql> select a.i as AI, a.b as AB, b.i as BI, b.b as BB from a left join b using (i) union select a.i as AI, a.b as AB, b.i as BI, b.b as BB from b left join a using (i) where b.i = 5;
+------+----------+------+------+
| AI | AB | BI | BB |
+------+----------+------+------+
| 1 | Not NULL | 1 | 0 |
| 2 | Not NULL | 2 | 0 |
| 3 | Not NULL | 3 | 0 |
| 4 | Not NULL | 4 | 0 |
| 0 | Not NULL | NULL | NULL |
| NULL | NULL | 5 | 0 |
+------+----------+------+------+
6 rows in set (0.00 sec)
is there anyway the column AB where = 0 based on the fact that BI = NULL within a joined statement (as the row obviously doesn't exist), and then vice verse, update BB where AI = NULL?
My instinct is NO!!! for a couple of reasons which I am looking to debunk or verify.
for the same reason you cannot update a real table with a temptable algorithm based view. I do not believe that you can update a table from the output of a derived table (at least not in such a way as I am looking for).
probably the most important reason, you cannot update something based on something else that doesn't exist (or by definition may exist but is an unknown value) due to the fact that without the isnull() function you cannot compare null. That being said to use the isnull() function you would need to have a static table with NULL in it, however due to the fact that in this particular instance the NULL values are only in affect when the tables are joined together (due to the obvious reason that matching rows plainly just do not exist). To perform this effectively you would need to make the values real by creating a derived table... which leads us back to point number 1.
If this IS possible and someone knows how to accomplish this please let me know... also if so is it possible to update both of the values with one statement?
I was about half finished with this command when I realized that it may be useless based on knowledge and other research.
mysql> update a,b set b.b='n',a.b='n' where /*possibly a derived table here*/ = isnull((select i1 as 'a.i', i2 as 'b.i' from ((select a.i as i1, b.i as i2 from a left join b using (i) where isnull(b.i)=1) union (select a.i as i1, b.i as i2 from b left join a using (i) where isnull(a.i)=1) as derived1 where isnull(i1)) /*maybe an or statement of some kind and another two derived tables here equalling each other*/ ;
based on my statement it seems that I would need another derived table or 3 to complete the statement in any kind of complete fashion... although I am not confident that it can work based on my tries up till now.
Also its possible I have been at this for long enough that I am no longer thinking straight.
In any case I appreciate the help in advance (even a this is possible, here is the direction you need to go).
Let me know if I need to provide any further information.
P.S. I do not want to put too much emphasis on my unfinished MySQL statement as It may not even be in the direction that I need to be going... and thus confusing.
I have found a partial answer. Please let me know if anyone can extend this. also I had to reinstall everything due to upgrading to centos 7.
where:
mysql> (select b.i as '1',b.b as '2',a.i as '3',a.b as '4' from a left join b using (i)) union (select b.i as '1',b.b as '2',a.i as '3',a.b as '4' from b left join a using (i)) order by 1;
+------+--------------+------+--------------+
| 1 | 2 | 3 | 4 |
+------+--------------+------+--------------+
| NULL | NULL | 1 | 何もない |
| 2 | 何もない | 2 | 何もない |
| 3 | 何もない | 3 | 何もない |
| 4 | 何もない | 4 | 何もない |
| 5 | 何もない | 5 | 何もない |
| 6 | 何もない | NULL | NULL |
+------+--------------+------+--------------+
6 rows in set (0.00 sec)
The command would end up using the same join syntax:
mysql> update a right join b using (i) set b.b = 'ある' where isnull(a.i) or isnull(b.i);
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
In this case MySQL was able to properly locate the row in a that was missing a match in b and replace the data as specified.
mysql> (select b.i as '1',b.b as '2',a.i as '3',a.b as '4' from a left join b using (i)) union (select b.i as '1',b.b as '2',a.i as '3',a.b as '4' from b left join a using (i)) order by 1;
+------+--------------+------+--------------+
| 1 | 2 | 3 | 4 |
+------+--------------+------+--------------+
| NULL | NULL | 1 | 何もない |
| 2 | 何もない | 2 | 何もない |
| 3 | 何もない | 3 | 何もない |
| 4 | 何もない | 4 | 何もない |
| 5 | 何もない | 5 | 何もない |
| 6 | ある | NULL | NULL |
+------+--------------+------+--------------+
6 rows in set (0.00 sec)
I'm not sure if this is the best answer for this question or it can get more detailed yet. that is something I would have to do more research on. If this is the best answer please let me know. If not and someone knows the best answer please let me know.

Joining from another table multiple times in a MySQL query

I am trying to do multiple joins on the same MySQL table, but am not getting the results that I expect to get. Hopefully someone can point out my mistake(s).
Table 1 - cpe Table
|id | name
|----------
| 1 | cat
| 2 | dog
| 3 | mouse
| 4 | snake
-----------
Table 2 - AutoSelect
|id | name | cpe1_id | cpe2_id | cpe3_id |
|-----------------------------------------------
| 1 | user1 | 1 | 3 | 4 |
| 2 | user2 | 3 | 1 | 2 |
| 3 | user3 | 3 | 3 | 2 |
| 4 | user4 | 4 | 2 | 1 |
------------------------------------------------
I would like to see an output of
user1 | cat | mouse | snake |
user2 | mouse | snake | dog |
..etc
Here is what I have tried
SELECT * FROM AutoSelect
LEFT JOIN cpe ON
( cpe.id = AutoSelect.cpe1_id ) AND
( cpe.id = AutoSelect.cpe2_id ) AND
( cpe.id = AutoSelect.cpe3_id )
I get blank results. I thought i knew how to do these joins, but apparently when I'm trying to match cpe?_id with the name of the cpe table.
Thanks in advance for any assistance.
You need left join 3 times as well. Currently your query only joins 1 time with 3 critieria as to the join. This should do:
SELECT a.name, cpe1.name, cpe2.name, cpe3.name FROM AutoSelect as a
LEFT JOIN cpe as cpe1 ON ( cpe1.id = a.cpe1_id )
LEFT JOIN cpe as cpe2 ON ( cpe2.id = a.cpe2_id )
LEFT JOIN cpe as cpe3 ON ( cpe3.id = a.cpe3_id )
And you probably mean to INNER JOIN rather than LEFT JOIN unless NULL values are allowed in your AutoSelect table.
I think your design is wrong.
With tables like that, you get it the way it's meant to be in relational databases :
table 1 : animal
id name
1 cat
2 dog
3 mouse
4 snake
table 2 : user
|id | name |
|--------------
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
| 4 | user4 |
table 3 : association
|id_user | id_animal|
|--------------------
| 1 | 1 |
| 1 | 3 |
| 1 | 4 |
| 2 | 3 |
| 2 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 2
| 4 | 4 |
| 4 | 2 |
| 4 | 1 |
---------------------
Then :
select u.name, a.name from user u, animal a, association ass where ass.user_id = u.id and ass.animal_id = a.id;
In this case, your solution won't produce a good dynamic database. There are other ways to make combinations of multiple tables. I can show you by my own database what you should use and when you should use this solution. The scheme is in dutch, but you'll probably understand the keywords.
Like you, I had to combine my windmills with a kWh-meter, which has to measure the energyproduction of my windmills. What you should do, is this case, is making another table(in my case molenkWhlink). Make sure your tables are INNODB-types(for making Foreign keys). What I've done is combining my meters and mills by putting a pointer(a foreign key) of their ID(in Dutch Volgnummer) in the new table. An advantage you may not need, but I certainly did, is the fact I was able to extend the extra table with connection and disconnection info like Timestamps and metervalues when linking or unlinking. This makes your database way more dynamic.
In my case, I Also had a table for meassurements(metingoverzicht). As you can see in the scheme, I've got 2 lines going from Metingoverzicht to molenkwhlink. The reason for this is quite simple. All meassurements I take, will be saved in table Metingoverzicht. Daily meassurements(which are scheduled) will have a special boolean put on, but unscheduled meassurements, will also me saved here, with the bollean turned off. When switching meters, I need the endvalue from the leaving meter and the startvalue from the new meter, to calculate the value of todays eneryproduction. This is where your solution comes in and an extra table won't work. Usually, when you need just one value from another table a JOIN will be used. The problem in this case is, I've got 2 meassurementIDs in 1 link(1 for connecting and 1 for disconnecting). They both point to the same tablecolumn, because they both need to hold the same type of information. That is when you can use a double JOIN from one table towards the other. Because, both values will only be used once, and just needed to be saved in a different place to avoid having 1 action stored on different locations, which should always be avoided.
http://s1101.photobucket.com/user/Manuel_Barcelona/media/schemedatabase.jpg.html