Preferential Select Query - mysql

The issue that we are trying to tackle is best shown with the following illustrative example:
CREATE TABLE table_1
(
id INT UNSIGNED AUTO_INCREMENT,
colA INT,
colB VARCHAR(10),
PRIMARY KEY(id)
);
CREATE TABLE table_2
(
id INT UNSIGNED AUTO_INCREMENT,
colY INT,
colZ VARCHAR(10),
PRIMARY KEY(id)
);
INSERT INTO table_1(colA, colB) VALUES(1, 'NPD5A6V9EI'), (2, 'ISO4IK42YQ'), (4, 'J12QAN4O42'), (6,'V8YTZFHCU4');
INSERT INTO table_2(colY, colZ) VALUES(3, 'RBUNWLO753'), (4, 'X2BCEY7O8B'), (5, 'BNUS7R4225'), (6, '72NOWCTH5G');
We would like to select our result based on the value of colA in table_1 but if that does not return a result , we would like to return our result based on the value of colY in table_2. In other words SELECTing from table_2 is the backup for SELECTing from table_1. The query returns NULL only if neither table satisfies the condition.
A pseudo SQL query could be:
SELECT colB FROM table_1 where colA = 3 OR SELECT colZ FROM table_2 where colY = 3;
The query should return output based on the following I/O table:
I O
= =
1 NPD5A6V9EI -- From table_1
2 ISO4IK42YQ -- From table_1
3 RBUNWLO753 -- From table_2
4 J12QAN4O42 -- From table_1 (has precedence over table_2 entry)
5 BNUS7R4225 -- From table_2
6 V8YTZFHCU4 -- From table_1 (has precedence over table_2 entry)
9 NULL
Kindly suggest solutions that:
make use of the latest DB features (for posterity)
work with MySQL version 5.6.51 (for our application)

Write a subquery that generates all the I rows that you want.
Then left join this with the two tables, and use IFNULL to take the matching value from table_1 in preference to table_2.
SELECT ids.id AS I, IFNULL(t1.colB, t2.colZ) AS O
FROM (SELECT 1 AS id UNION ALL SELECT 2 UNION ALL SELECT 3 ... UNION ALL SELECT 9) AS ids
LEFT JOIN table_1 AS t1 ON t1.colA = ids.id
LEFT JOIN table_2 AS t2 ON t2.colY = ids.id
ORDER BY ids.id

I simply don't kn ow where you get your last row.
also with Myql 8 you can ise the window function ROW_NUMBER
the rest is self explantory, the sorting comes from colA and Col1, when there are teh same numbers the second column orderby2 comes and sorts first for the first table
CREATE TABLE table_1
(
id INT UNSIGNED AUTO_INCREMENT,
colA INT,
colB VARCHAR(10),
PRIMARY KEY(id)
);
CREATE TABLE table_2
(
id INT UNSIGNED AUTO_INCREMENT,
colY INT,
colZ VARCHAR(10),
PRIMARY KEY(id)
);
INSERT INTO table_1(colA, colB) VALUES(1, 'NPD5A6V9EI'), (2, 'ISO4IK42YQ'), (4, 'J12QAN4O42'), (6,'V8YTZFHCU4');
INSERT INTO table_2(colY, colZ) VALUES(3, 'RBUNWLO753'), (4, 'X2BCEY7O8B'), (5, 'BNUS7R4225'), (6, '72NOWCTH5G');
SELECT #i := #i +1 AS I,
colB AS O
FROM
(SELECT colA as orderby1,colB,1 ordberby2 froM table_1
UNION
SELECT colY, colZ,2 froM table_2 ) t1,(SELECT #i := 0) t2
ORDER BY orderby1,ordberby2
I | O
-: | :---------
1 | NPD5A6V9EI
2 | ISO4IK42YQ
3 | RBUNWLO753
4 | J12QAN4O42
5 | X2BCEY7O8B
6 | BNUS7R4225
7 | V8YTZFHCU4
8 | 72NOWCTH5G
db<>fiddle here

Related

Merging two result sets

I know there are a number of questions here relating to what I am facing, but none of them are able to solve my situation.
I have two tables TABLE_1 and TABLE_2.
Table TABLE_1 has columns:
ID,
NAME
Table TABLE_2 has columns:
CODE,
AMOUNT,
QUANTITY
The two tables have a different number of columns but the row count is same.
Is possible to write an SQL query wherein I can retrieve all the columns from both the table in a single result set.
I am working on MySQL server.
Note: Both the tables have no common column. Any help is appreciated.
This is how I wish to retrieve:
| ID | NAME | CODE | AMOUNT | QUANTITY |
| | | | | |
| | | | | |
| | | | | |
Refer below query
SELECT a.ID, a.NAME, b.CODE,b.AMOUNT ,b.QUANTITY
FROM (SELECT
ROW_NUMBER() OVER(ORDER BY name ASC) AS RowNo, * FROM TABLE_1 )a
inner join (SELECT
ROW_NUMBER() OVER(ORDER BY CODE ASC) AS RowNo, * FROM TABLE_2 )b
On a.RowNo= b.RowNo
#Allan, here is my solution. Hope it helps.
CREATE TABLE t1(
ID INTEGER,
NAME VARCHAR(10)
);
CREATE TABLE t2(
CODE INTEGER,
AMOUNT INTEGER,
QUANTITY INTEGER
);
INSERT INTO t1 VALUES(91, 'Name1');
INSERT INTO t1 VALUES(92, 'Name2');
INSERT INTO t1 VALUES(93, 'Name3');
INSERT INTO t2 VALUES(1, 123, 2);
INSERT INTO t2 VALUES(2, 233, 4);
INSERT INTO t2 VALUES(3, 433, 1);
Query:
SET #rank=0;
SET #rank2=0;
select id,name,code,amount,quantity
from
(SELECT #rank:=#rank+1 AS rank, id, name
from t1) a,
(SELECT #rank2:=#rank2+1 AS rank, code, amount, quantity
from t2) b
where a.rank=b.rank;

Referencing column depending on Ids of another column in sequence

I am trying to update (reference) a column (oid) of one table with OID of another table's column with certain condition.
Example :
Customer Table :
------------------
CID name oid
-------------------
1 abc null
2 abc null
3 abc null
4 xyz null
--------------------
Order Table
--------------
OID name
--------------
10 abc
11 abc
12 abc
13 xyz
--------------
Ouput should be :
Customer Table :
------------------
CID name oid
-------------------
1 abc 10
2 abc 11
3 abc 12
4 xyz 13
--------------------
I have tried the following
UPDATE customer as c, order as o
SET c.oid = o.OID
WHERE c.name = o.name;
-----------------------------
update customer INNER JOIN order on customer.name=Order.name
SET customer.oid=Order.OID
where customer.oid IS null;
But the customer table is being updated as follows
Customer Table :
------------------
CID name oid
-------------------
1 abc 10
2 abc 10
3 abc 10
4 xyz 13
--------------------
The idea is to assign a row number to each of the entries in Customer table and Order table.
Thus when making an inner join between these two tables you have two conditions right now (whereas previously it was one i.e. only name).
One condition is name and another one is the row_number
You can go with this query:
UPDATE Customer CT
INNER JOIN (
SELECT
customerTable.CID,
orderTable.OID FROM
(
SELECT
*,
#rn1 := #rn1 + 1 AS row_number
FROM
Customer C
CROSS JOIN (SELECT #rn1 := 0) var
ORDER BY CID
) AS customerTable
INNER JOIN (
SELECT
*,
#rn2 := #rn2 + 1 AS row_number
FROM
`Order` O
CROSS JOIN (SELECT #rn2 := 0) var
ORDER BY OID
) AS orderTable ON customerTable. NAME = orderTable. NAME
AND customerTable.row_number = orderTable.row_number
) AS combinedTable ON CT.CID = combinedTable.CID
SET CT.oid = combinedTable.OID
Note: Since joining these two tables on matching name is not sufficient for what are you looking for. That's why besides matching name assign a row_number to each of the rows (both in Customer and Order table. Then make an inner join between these two tables on matching name and row number. Thus you are restricting one entry to be joined multiple times with other entries from another table.
TEST SCHEMA & DATA:
Couldn't add an sql fiddle
DROP TABLE IF EXISTS `customer`;
CREATE TABLE `customer` (
`CID` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`oid` int(11) DEFAULT NULL,
PRIMARY KEY (`CID`)
);
INSERT INTO `customer` VALUES ('1', 'abc', null);
INSERT INTO `customer` VALUES ('2', 'abc', null);
INSERT INTO `customer` VALUES ('3', 'abc', null);
INSERT INTO `customer` VALUES ('4', 'xyz', null);
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`OID` int(11) NOT NULL,
`name` varchar(100) NOT NULL
);
INSERT INTO `order` VALUES ('10', 'abc');
INSERT INTO `order` VALUES ('11', 'abc');
INSERT INTO `order` VALUES ('12', 'abc');
INSERT INTO `order` VALUES ('13', 'xyz');
See now, how does the Customer table look like:
SELECT
*
FROM Customer;
Output:
CID name oid
1 abc 10
2 abc 11
3 abc 12
4 xyz 13
This is very complicated to do. You need to assign a counter variable to each value -- which is a bit painful in an update statement. But something like this should work:
update customer c join
(select c.*,
(#rn := if(#n = name, #rn + 1,
if(#n := name, 1, 1)
)
) as rn
from customer c cross join
(select #n := '', #rn := 0) params
order by name, cid
) cc
on c.cid = cc.cid join
(select o.*,
(#rno := if(#no = name, #rno + 1,
if(#no := name, 1, 1)
)
) as rn
from orders o cross join
(select #no := ', #rno := 0) params
) o
on c.name = o.name and c.rn = o.rn
set c.oid = o.oid;

Insert records to a table with loop from an other table data

I have two tables like this:
SupplyList (IDSupply is the primary key)
IDSupply PartName Qty
--------- --------- ----
1 C 10
2 B 4
SupplyIndex (IDSupply and Index are the compound primary key)
IDSupply PartName Index
--------- --------- ------
1 C 2
1 C 3
1 C 7
1 C 9
1 C 10
These tables are related to each other with IDSupply.
I want to insert missed records to SupplyIndex table by a query in SQL. In other words, my expected result is SupplyIndex table like below (Index must include numbers from 1 to Qty from SupplyList table)
IDSupply PartName Index
--------- --------- ------ (result)
1 C 1
1 C 2
1 C 3
1 C 4
1 C 5
1 C 6
1 C 7
1 C 8
1 C 9
1 C 10
2 B 1
2 B 2
2 B 3
2 B 4
I did this job in my VB.Net application before and now I want to do it in SQL Server directly.
Would you please help me?
Thanks
Test Data:
create table #supplylist
(
idsupply int,
partname char(20),
qty int
)
insert into #supplylist
select 1,'a',10
union all
select 2,'c',4
create table #idsupply
(
idsupply int,
partname char(20),
indexx int
)
insert into #idsupply
select 1,'a',10
union all
select 2,'c',3
I used Numbers table to accomplish this
with cte
as
(
select
idsupply,
partname,n
from
#supplylist t
cross apply
(
select n from numbers where n <=qty
)b
--final part to check and isnert in other table..same query as above with insert and exists
with cte
as
(
select
idsupply,
partname,n
from
#supplylist t
cross apply
(
select n from numbers where n <=qty
)b
)
insert into #idsupply
select * from cte t1 where not exists (select 1 from #idsupply t2 where t2.indexx=t1.n)
Create Table #SupplyList(IDSupply int Primary Key, PartName char(10),Qty int);
Insert #SupplyList(IDSupply, PartName, Qty) Values
(1, 'C', 10),
(2, 'B', 4);
Create Table #SupplyIndex(IDSupply int, PartName char(10), [Index] int Primary Key (IdSupply, [Index]));
Insert #SupplyIndex(IDSupply, PartName, [Index]) Values
(1, 'C', 2),
(1, 'C', 3),
(1, 'C', 7),
(1, 'C', 9),
(1, 'C', 10);
;With cteMax As
(Select Max([Index]) As MaxIndex From #SupplyIndex),
cteNumbers As
(Select 1 As Number
Union All
Select Number + 1
From cteNumbers n
Cross Join cteMax m
Where n.Number < m.MaxIndex)
Merge #SupplyIndex t
Using (Select sl.IDSupply, sl.PartName, n.Number As Qty From #SupplyList sl Inner Join cteNumbers n On n.Number <= sl.Qty) s
On s.IDSupply = t.IDSupply And s.PartName = t.PartName And s.Qty = t.[Index]
When Not Matched Then Insert(IDSupply, PartName, [Index]) Values(s.IDSupply, s.PartName, s.Qty);
Select * From #SupplyIndex;
go
Drop Table #SupplyList;
go
Drop Table #SupplyIndex

Joining tables doubles the values

I have this table:
CREATE TABLE table1 (
id INT NOT NULL PRIMARY KEY,
value1 INT NOT NULL,
value2 INT NOT NULL
);
CREATE TABLE table2 (
id INT NOT NULL PRIMARY KEY,
table1_id INT NOT NULL,
valuex INT NOT NULL
);
INSERT INTO table1 (id, value1, value2)
VALUES
(1, 10, 15),
(2, 5 , 3);
INSERT INTO table2 (id, table1_id, valuex)
VALUES
(1, 1, 15),
(2, 1, 25),
(3, 2, 14),
(4, 2, 10);
With this:
SELECT COUNT(`table1`.`id`) AS `orders`,
SUM(`value1`) as `sum_value1`, SUM(`value2`) as `sum_value2`,
SUM(`valuex`) as `sum_valuex`
FROM `table1`
INNER JOIN `table2`
ON `table1`.`id` = `table2`.`table1_id`
I get the output:
+----------------------------------------------+
+ orders | sum_value1 | sum_value2 |sum_valuex +
+----------------------------------------------+
+ 4 | 30 | 36 | 64 +
+----------------------------------------------+
But I have only two orders in table1. I know the duplication is being made because of the join, but how can I fix that with adding sum_valuex?
My desired result would be:
+----------------------------------------------+
+ orders | sum_value1 | sum_value2 |sum_valuex +
+----------------------------------------------+
+ 2 | 15 | 18 | 64 +
+----------------------------------------------+
EDIT: I can't use select within select
This is how joins work. If you don't want the rows to multiply before the aggregation, then aggregate before doing the join.
SELECT t2.orders, t1.value1, t1.value2, t2.sum_valuex
FROM `table1` INNER JOIN
(SELECT table1_id, SUM(valuex) as sum_valuex, COUNT(*) as orders
FROM table2
GROUP BY table1_id
) t2
ON t1.id = t2.table1_id
Which table has the orders ? Right now, your count(table1.id) is counting the records in table 2. (That's where this column is, in Table 2) if Table1 is the table with the orders in it then you should be counting the records in table 1
SELECT COUNT(distinct a.id) orders,
SUM(value1) as sum_value1`, SUM(value2) as sum_value2,
SUM(valuex) as sum_valuex
FROM table1 a
JOIN table2 b
ON b.table1_id = a.id
you can get the desire result like this :
SELECT count(orders ) as orders, SUM(t1.value1) as value1, SUM(t1.value2) as value2, SUM(t2.sum_valuex) as sumvaluex
FROM table1 t1 INNER JOIN
(SELECT table1_id, SUM(valuex) as sum_valuex, COUNT(*) as orders
FROM table2
GROUP BY table1_id
) t2
ON t1.id = t2.table1_id
Please dont forget to mark my answer:)

How does NOT IN subquery work with NULL values?

I am confused how the following works in MySQL. In the queries below, the first SELECT returns all rows from table2 while the second SELECT returns none of the rows. Is there an explanation of how NULL works with the NOT IN operator. Is there any documentation to explains this?
CREATE TABLE table1 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
CREATE TABLE table2 (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
table1_id INT UNSIGNED,
PRIMARY KEY (id)
);
INSERT INTO table2 (id, table1_id) VALUES (1, NULL);
SELECT COUNT(*) FROM table2 WHERE table1_id NOT IN (SELECT id FROM table1);
+----------+
| COUNT(*) |
+----------+
| 1 |
+----------+
INSERT INTO table1 (id) VALUES (1);
SELECT COUNT(*) FROM table2 WHERE table1_id NOT IN (SELECT id FROM table1);
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
The reason is that according to the SQL specification, Foo IN(A,B,C) translates to ( Foo = A Or Foo = B Or Foo = C ). Thus, if we have Foo In(Null, 1, 2) we get Foo = Null Or Foo = 1 Or Foo = 2. Since Foo = Null is always UNKNOWN and evaluated to False for purposes of filtering, Nulls in your IN expression will return no results.
You can add IFNULL(id, '') to sub-query so it will work, example:
SELECT COUNT(*) FROM table2 WHERE table1_id NOT IN (SELECT IFNULL(id, '') FROM table1);