Using UNION in prepared statements in PHP/MySQL [duplicate] - php

I have two tables (Table A and Table B).
These have different number of columns - Say Table A has more columns.
How can I union these two table and get null for the columns that Table B does not have?

Add extra columns as null for the table having less columns like
Select Col1, Col2, Col3, Col4, Col5 from Table1
Union
Select Col1, Col2, Col3, Null as Col4, Null as Col5 from Table2

I came here and followed above answer. But mismatch in the Order of data type caused an error. The below description from another answer will come handy.
Are the results above the same as the sequence of columns in your table? because oracle is strict in column orders. this example below produces an error:
create table test1_1790 (
col_a varchar2(30),
col_b number,
col_c date);
create table test2_1790 (
col_a varchar2(30),
col_c date,
col_b number);
select * from test1_1790
union all
select * from test2_1790;
ORA-01790: expression must have same datatype as corresponding expression
As you see the root cause of the error is in the mismatching column ordering that is implied by the use of * as column list specifier. This type of errors can be easily avoided by entering the column list explicitly:
select col_a, col_b, col_c from test1_1790
union all
select col_a, col_b, col_c from test2_1790;
A more frequent scenario for this error is when you inadvertently swap (or shift) two or more columns in the SELECT list:
select col_a, col_b, col_c from test1_1790
union all
select col_a, col_c, col_b from test2_1790;
OR if the above does not solve your problem, how about creating an ALIAS in the columns like this: (the query is not the same as yours but the point here is how to add alias in the column.)
SELECT id_table_a,
desc_table_a,
table_b.id_user as iUserID,
table_c.field as iField
UNION
SELECT id_table_a,
desc_table_a,
table_c.id_user as iUserID,
table_c.field as iField

Normally you need to have the same number of columns when you're using set based operators so Kangkan's answer is correct.
SAS SQL has specific operator to handle that scenario:
SAS(R) 9.3 SQL Procedure User's Guide
CORRESPONDING (CORR) Keyword
The CORRESPONDING keyword is used only when a set operator is specified. CORR causes PROC SQL to match the columns in table expressions by name and not by ordinal position. Columns that do not match by name are excluded from the result table, except for the OUTER UNION operator.
SELECT * FROM tabA
OUTER UNION CORR
SELECT * FROM tabB;
For:
+---+---+
| a | b |
+---+---+
| 1 | X |
| 2 | Y |
+---+---+
OUTER UNION CORR
+---+---+
| b | d |
+---+---+
| U | 1 |
+---+---+
<=>
+----+----+---+
| a | b | d |
+----+----+---+
| 1 | X | |
| 2 | Y | |
| | U | 1 |
+----+----+---+
U-SQL supports similar concept:
OUTER UNION BY NAME ON (*)
OUTER
requires the BY NAME clause and the ON list. As opposed to the other set expressions, the output schema of the OUTER UNION includes both the matching columns and the non-matching columns from both sides. This creates a situation where each row coming from one of the sides has "missing columns" that are present only on the other side. For such columns, default values are supplied for the "missing cells". The default values are null for nullable types and the .Net default value for the non-nullable types (e.g., 0 for int).
BY NAME
is required when used with OUTER. The clause indicates that the union is matching up values not based on position but by name of the columns. If the BY NAME clause is not specified, the matching is done positionally.
If the ON clause includes the “*” symbol (it may be specified as the last or the only member of the list), then extra name matches beyond those in the ON clause are allowed, and the result’s columns include all matching columns in the order they are present in the left argument.
And code:
#result =
SELECT * FROM #left
OUTER UNION BY NAME ON (*)
SELECT * FROM #right;
EDIT:
The concept of outer union is supported by KQL:
kind:
inner - The result has the subset of columns that are common to all of the input tables.
outer - The result has all the columns that occur in any of the inputs. Cells that were not defined by an input row are set to null.
Example:
let t1 = datatable(col1:long, col2:string)
[1, "a",
2, "b",
3, "c"];
let t2 = datatable(col3:long)
[1,3];
t1 | union kind=outer t2;
Output:
+------+------+------+
| col1 | col2 | col3 |
+------+------+------+
| 1 | a | |
| 2 | b | |
| 3 | c | |
| | | 1 |
| | | 3 |
+------+------+------+
demo

if only 1 row, you can use join
Select t1.Col1, t1.Col2, t1.Col3, t2.Col4, t2.Col5 from Table1 t1 join Table2 t2;

Related

Join two tables based on table_1 column value to table_2 column name that matches?

Is it possible to join 2 tables together on the basis of table 1 column value equaling table_2 column name?
table_1 table_2
----- -------------
| A | | 1 | 2 | 3 |
|---| |-----------|
| 1 | | Q | W | E |
----- -------------
Desired result is:
Select A from table_1 join table_2 WHERE table_1 A = table_2 column name.
Result:
1Q
I am using PHP, sorry for the poor psudo SQL, I honestly have no idea how to word it. Thank you for any help you can offer!
You want SQL where you use a value from one column as the name of another column. You Can't Do That™. In pure SQL queries the names of tables and columns must be literals (using the lingo of php style languages.
You probably can get the result you need by reading your tables into RAM with your php program, then programming your table-to-table correlation.
SELECT CONCAT(table_1.A,"",table_2.1) as RESULT
FROM table_1
LEFT JOIN table_2 ON table_1.A = table_2.1

How to get one colum from the join of 2 tables SQL

I have 2 tables like this:
Table1
id | name
------------------
1 | David
2 | Lucas
3 | Antonio
Table2
id | name
------------------
1 | Sergio
2 | Sergio
3 | Lucas
I want to select data to group duplicate records and return this:
name
------------------
David
Lucas
Antonio
Sergio
So I tried with this query
SELECT name FROM Table1 JOIN Table2 GROUP BY name
But nothing is returned.
You need UNION instead of JOIN :
SELECT name
FROM table1
UNION
SELECT name
FROM table2;
JOIN is used for matching rows and produce subsequent columns from joined tables while union will combine all rows from two or more tables.
use union
select name fron table1
union
select name from table2

Find unmatched results obtained by two different queries on two different tables [duplicate]

I've got the following two tables (in MySQL):
Phone_book
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1 | John | 111111111111 |
+----+------+--------------+
| 2 | Jane | 222222222222 |
+----+------+--------------+
Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 1 | 0945 | 111111111111 |
+----+------+--------------+
| 2 | 0950 | 222222222222 |
+----+------+--------------+
| 3 | 1045 | 333333333333 |
+----+------+--------------+
How do I find out which calls were made by people whose phone_number is not in the Phone_book? The desired output would be:
Call
+----+------+--------------+
| id | date | phone_number |
+----+------+--------------+
| 3 | 1045 | 333333333333 |
+----+------+--------------+
There's several different ways of doing this, with varying efficiency, depending on how good your query optimiser is, and the relative size of your two tables:
This is the shortest statement, and may be quickest if your phone book is very short:
SELECT *
FROM Call
WHERE phone_number NOT IN (SELECT phone_number FROM Phone_book)
alternatively (thanks to Alterlife)
SELECT *
FROM Call
WHERE NOT EXISTS
(SELECT *
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number)
or (thanks to WOPR)
SELECT *
FROM Call
LEFT OUTER JOIN Phone_Book
ON (Call.phone_number = Phone_book.phone_number)
WHERE Phone_book.phone_number IS NULL
(ignoring that, as others have said, it's normally best to select just the columns you want, not '*')
SELECT Call.ID, Call.date, Call.phone_number
FROM Call
LEFT OUTER JOIN Phone_Book
ON (Call.phone_number=Phone_book.phone_number)
WHERE Phone_book.phone_number IS NULL
Should remove the subquery, allowing the query optimiser to work its magic.
Also, avoid "SELECT *" because it can break your code if someone alters the underlying tables or views (and it's inefficient).
The code below would be a bit more efficient than the answers presented above when dealing with larger datasets.
SELECT *
FROM Call
WHERE NOT EXISTS (
SELECT 'x'
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number
);
SELECT DISTINCT Call.id
FROM Call
LEFT OUTER JOIN Phone_book USING (id)
WHERE Phone_book.id IS NULL
This will return the extra id-s that are missing in your Phone_book table.
I think
SELECT CALL.* FROM CALL LEFT JOIN Phone_book ON
CALL.id = Phone_book.id WHERE Phone_book.name IS NULL
SELECT t1.ColumnID,
CASE
WHEN NOT EXISTS( SELECT t2.FieldText
FROM Table t2
WHERE t2.ColumnID = t1.ColumnID)
THEN t1.FieldText
ELSE t2.FieldText
END FieldText
FROM Table1 t1, Table2 t2
SELECT name, phone_number FROM Call a
WHERE a.phone_number NOT IN (SELECT b.phone_number FROM Phone_book b)
Alternatively,
select id from call
minus
select id from phone_number
Don't forget to check your indexes!
If your tables are quite large you'll need to make sure the phone book has an index on the phone_number field. With large tables the database will most likely choose to scan both tables.
SELECT *
FROM Call
WHERE NOT EXISTS
(SELECT *
FROM Phone_book
WHERE Phone_book.phone_number = Call.phone_number)
You should create indexes both Phone_Book and Call containing the phone_number. If performance is becoming an issue try an lean index like this, with only the phone number:
The fewer fields the better since it will have to load it entirely. You'll need an index for both tables.
ALTER TABLE [dbo].Phone_Book ADD CONSTRAINT [IX_Unique_PhoneNumber] UNIQUE NONCLUSTERED
(
Phone_Number
)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = ON) ON [PRIMARY]
GO
If you look at the query plan it will look something like this and you can confirm your new index is actually being used. Note this is for SQL Server but should be similar for MySQL.
With the query I showed there's literally no other way for the database to produce a result other than scanning every record in both tables.

Join the values of 2 tables

I have 2 tables and I can't merge the values of these tables into table2.
Table1:
id | studnum | fname | lname | mname
1 | 1001 | Mark | Lei | Ramos
Table2:
id | studnum | remarks
Is it possible that when I input the values into Table2, the studnum from Table1 will input into Table2 also?
I tried this but it doesn't worked.
$sql = "SELECT table1.studnum FROM table1, table2
WHERE table1.studnum = table2.studnum";
So that the Table2 will be like this output
id | studnum | remarks
1 | 1001 | good
What you're looking for is using a JOIN Statement.
If the shared key is between them is studnum, you can do
SELECT * FROM table1
JOIN table2
ON table1.studnum = table2.studnum
Try a MySQL join function. Example:
SELECT *
FROM table1
JOIN table2
ON table1.studnum=table2.studnum;
More reading on joins: http://www.keithjbrown.co.uk/vworks/mysql/mysql_p5.php
If those are exactly your tables you dont really need what you are requesting as it goes against normalization.
In the case you will only have one or none remarks for an entry in table 1 there is no need for table 2, the remarks should be in table 1.
In the case you need multiple remarks for a single entry in table 1, you dont need stdnum in table 2. If your problem in this scenario is to construct a query to get the remakrs based on stdnums you can easily achieve this with a joint.
The only field you should be duplicating from table to table for a record/object is the id. You can (and may be should) scrifice normalization in pro of efficiency but your example is not the case.

Returning values even the result are empty

I have a table that is similar below.
| user_id | point_1 | point_2 | point_3
453123 1234 32 433
321543 1 213 321
My query is something like this:
$query = "SELECT * FROM my_table WHERE user_id = 12345 OR user_id = 987654321"
Obviously, this will return nothing since user_id 12345 OR user_id 987654321 do not exist on the table.
But I still want to return something like the one below :
| user_id | point_1 | point_2 | point_3
12345 0 0 0
987654321 0 0 0
You could use an inline view as a rowsource for your query. To return a zero in place of a NULL (which would be returned by the outer join when no matching row is found in my_table, you can use the IFNULL function.
e.g.
SELECT s.user_id
, IFNULL(t.point_1,0) AS point_1
, IFNULL(t.point_2,0) AS point_2
, IFNULL(t.point_3,0) AS point_3
FROM ( SELECT 12345 AS user_id
UNION ALL SELECT 987654321
) s
LEFT
JOIN my_table t
ON t.user_id = s.user_id
NOTE: If datatype of user_id column my_table is character, then I'd enclose the literals in the inline view in single quotes. e.g. SELECT '12345' AS user_id. If the characterset of the column doesn't match your client characterset, e.g. database column is latin1, and client characterset is UTF8, you'd want to force the character strings to be a compatible (coercible) characterset... SELECT _latin1'12345' AS user_id
You can't get the result you want using only a select statement. Only rows that exist somewhere will be returned.
The only way I can think to do this is to insert the query values into a temp table and then outer join against that for your query.
So the basic process would be:
create table temp1 (user_id integer);
insert into temp1 values (987654321); -- repeat as needed for query.
select t.user_id, m.* from temp1 t left outer join my_table m on m.user_id = t.user_id;
drop table temp1;
This isn't very efficient though.
Your desired result resembles the result of an OUTER JOIN - when some records exist only in one table and not the other, an OUTER JOIN will show all of the rows from one of the joined tables, filling in missing fields from the other table with NULL values.
To solve your particular problem purely in SQL, you could create a second table that contains a single field with all of the user_id values that you want to be able to show in your result. Something like:
+-----------+
| user_id |
+-----------+
| 1 |
+-----------+
| 2 |
+-----------+
| 3 |
+-----------+
| ... |
+-----------+
| 12344 |
+-----------+
| 12345 |
+-----------+
| 12346 |
+-----------+
| ... |
+-----------+
And so on. If this second table is named all_ids, you could then get your desired result by modifying your query as follows (exact syntax may vary by database implementation):
SELECT
*
FROM
all_ids AS i
LEFT OUTER JOIN
my_table AS t ON i.user_id = t.user_id
WHERE
i.user_id = 12345
OR i.user_id = 987654321;
This should produce the following result set:
+-----------+----------+----------+----------+
| user_id | point_1 | point_2 | point_3 |
+-----------+----------+----------+----------+
| 12345 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
| 987654321 | NULL | NULL | NULL |
+-----------+----------+----------+----------+
It should be noted that this table full of IDs could take up a significant amount of disk space. An integer column in MySQL can hold 4,294,967,296 4-byte values, or 16 GB of data sitting around purely for your convenience in displaying some other data you don't have. So unless you need some smaller range or set of IDs available, or have disk space coming out your ears, this approach simply may not be practical.
Personally, I would not ask the database to do this in the first place. Essentially it's a display issue; you already get all the information you need from the fact that certain rows were not returned by your query. I would solve the display issue outside of the database, which in your case means filling in those zeroes with PHP.

Categories