Order By Issue with MySQL vs SQL Server - mysql

I have one SQL query in SQL Server like this
SELECT
CASE WHEN ROW_NUMBER() OVER(PARTITION BY _no ORDER BY _no asc) = 1 THEN
_no ELSE '' END
as row_no,
_no,
_name,
r._names
FROM
(
SELECT '1' as _no, 'vikas' as _name UNION ALL
SELECT '1', 'kratika' UNION ALL
SELECT '2', 'vikas' UNION ALL
SELECT '1', 'kratika kastwar'
) t
INNER JOIN
(
SELECT '1' as _nos, 'One' as _names UNION ALL
SELECT '2', 'two'
) r
ON r._nos = t._no
ORDER BY _no
Output:
row_no _no _name _names
------ ---- --------------- ------
1 1 kratika One
1 kratika kastwar One
1 vikas One
2 2 vikas two
And the same I am doing in MySQL like this
SELECT
CASE WHEN _no = #i THEN '' ELSE #i := _no END
as row_no,
_no,
_name,
r._names
FROM
(
SELECT '1' as _no, 'vikas' as _name UNION ALL
SELECT '1', 'kratika' UNION ALL
SELECT '2', 'vikas' UNION ALL
SELECT '1', 'kratika kastwar'
) t
INNER JOIN
(
SELECT '1' as _nos, 'One' as _names UNION ALL
SELECT '2', 'two'
) r
ON r._nos = t._no
,
(SELECT #i := '') temp
ORDER BY _no
Output :
1 1 vikas One
1 kratika One
1 1 kratika kastwar One
2 2 vikas two
But I am expecting output in MySQL like this
1 1 kratika One
1 kratika kastwar One
1 vikas One
2 2 vikas two
I don't want to use query like this in MySQL as desc here MYSQL Order By W/Count
SELECT
CASE WHEN _no = #i THEN '' ELSE #i := _no END
as row_no,
_no,
_name,
_names
FROM
(SELECT
*
FROM
(
SELECT '1' as _no, 'vikas' as _name UNION ALL
SELECT '1', 'kratika' UNION ALL
SELECT '2', 'vikas' UNION ALL
SELECT '1', 'kratika kastwar'
) t
INNER JOIN
(
SELECT '1' as _nos, 'One' as _names UNION ALL
SELECT '2', 'two'
) r
ON r._nos = t._no
,
(SELECT #i := '') temp
ORDER BY _no) t
How I can achieve the same in MySQL, query performance is major parameter

I think what's happening here is that MySQL is constructing the resultset and then going through an extra step to order it according to the ORDER BY clause. Since the 'kratika kastwar' row comes after the row where _no is 2, you get the unexpected output.
The solution, I guess, would be to put the basic SELECT (without the special user-variable shenanigans) in a subquery in the FROM clause, applying the ORDER BY clause to the subquery. Then do the user-variable work in the outer query. That way the ordering has already happened.
Edit: I see that you said you don't want to do this. I don't think you have a choice, unless you can find a way to get MySQL to not do the ORDER BY by performing a filesort step on the computed results (very unlikely).

SELECT
CASE WHEN _no = #i THEN '' ELSE #i := _no END
as row_no,
_no,
_name,
_names
FROM
(SELECT
*
FROM
(
SELECT '1' as _no, 'vikas' as _name UNION ALL
SELECT '1', 'kratika' UNION ALL
SELECT '2', 'vikas' UNION ALL
SELECT '1', 'kratika kastwar'
) t
INNER JOIN
(
SELECT '1' as _nos, 'One' as _names UNION ALL
SELECT '2', 'two'
) r
ON r._nos = t._no
,
(SELECT #i := '')

Related

Trying to concatenate three different rows in mysql

So, I have an attributes table and I'm trying to build a query to concatenate into one column
attributes
------------
id, c_id, key, value
1, 1, 'day', 11
2, 1, 'month', 09
3, 1, 'year', 1999
4, 2, 'day', 14
5, 2, 'month', 11
6, 2, 'year', 2004
And this is the query I wrote,
SELECT
consumer_id,
CONCAT(
(SELECT `value` FROM consumer_attributes WHERE `key` = 'select_day'),
'_',
CONCAT(
(SELECT `value` FROM consumer_attributes WHERE `key` = 'select_month'),
'_',
CONCAT(
(SELECT `value` FROM consumer_attributes WHERE `key` = 'select_year'),
'',
''
)
)
) AS dob
FROM consumer_attributes
It throws out
ERROR CODE: 1242 Subquery returns more than 1 row
Can someone help me out?
output I'm trying to achieve
consumer_id, concat
1, 11_09_1999
2, 14_11_2004
SELECT c_id, CONCAT_WS('_',
(SELECT value FROM consumer_attributes a WHERE `key`='day' AND a.c_id = c.c_id),
(SELECT value FROM consumer_attributes a WHERE `key`='month' AND a.c_id = c.c_id),
(SELECT value FROM consumer_attributes a WHERE `key`='year' AND a.c_id = c.c_id)) AS dob
FROM (SELECT DISTINCT c_id FROM consumer_attributes) c;
https://www.db-fiddle.com/f/pB6b5xrgPKCivFWcpQHsyE/14
Try this,
SELECT `c_id`, CONCAT(GROUP_CONCAT(IF(`key` = 'day', `value`, NULL)),'_',GROUP_CONCAT(IF(`key` = 'month', `value`, NULL)),'_',GROUP_CONCAT(IF(`key` = 'year', `value`, NULL))) as dob
FROM `consumer_attributes`
GROUP BY `c_id`
Note: is it select_day or day ? You should change it above query if its different .
You didnt join subqueries with main query with id columns, so it finds more than one rows for each record . It should be ok if the rest is ok :
SELECT
dmy.consumer_id,
concat (
max(ifnull( (SELECT `value` FROM consumer_attributes dd WHERE `key` = 'day' and dd.id = dmy.id) , -1)) ,'_' ,
max(ifnull( (SELECT `value` FROM consumer_attributes mm WHERE `key` = 'month' and mm.id = dmy.id) , -1) ), '_' ,
max(ifnull( (SELECT `value` FROM consumer_attributes yy WHERE `key` = 'year' and yy.id = dmy.id) , -1)) )
FROM consumer_attributes dmy
group by dmy.consumer_id
Another approach (if you have a lot of data you might want to time different answers);
SELECT c_id, CONCAT(d.d, '_', m.m, '_', y.y) dob
FROM (select c_id, value d FROM consumer_attributes WHERE `key`='day') d
NATURAL JOIN (select c_id, value m FROM consumer_attributes WHERE `key`='month') m
NATURAL JOIN (select c_id, value y FROM consumer_attributes WHERE `key`='year') y;

Selecting first 4 numbers from a string MySQL

If I have strings like this:
CC123484556
CC492014512
BUXT122256690
How can I manipulate code like this in MySQL to pull the first 4 values that are numbers ? There are various # of letters before numbers in other rows but the most important thing are the first 4 numbers that show up.
SELECT LEFT(alloy , 4) FROM tbl
So the desired result would be:
1234
4920
1222
Slow and ugly:
SELECT col,
SUBSTRING(tab.col, MIN(LOCATE(four_digits, tab.col,1)), 4) + 0 AS result
FROM (SELECT 'CC123484556' AS col UNION ALL
SELECT 'CC492014512' UNION ALL
SELECT 'BUXT122256690' UNION ALL
SELECT 'abced') tab
CROSS JOIN (
SELECT CONCAT(d1.z, d2.z, d3.z, d4.z) AS four_digits
FROM (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d1
CROSS JOIN (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d2
CROSS JOIN (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d3
CROSS JOIN (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d4
) sub
WHERE LOCATE(four_digits, tab.col,1) > 0
GROUP BY col;
Rextester Demo
Generate all 4 digit combinations, locate them in string and get substring with lowest index.
EDIT:
A bit faster approach:
SELECT col, SUBSTRING(col, MIN(i), 4) + 0 AS r
FROM (
SELECT col, SUBSTRING(tab.col, i , 4) + 0 AS result, i
FROM tab
CROSS JOIN (
SELECT CONCAT(d1.z, d2.z)+1 AS i
FROM (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d1
CROSS JOIN (SELECT '1' AS z UNION SELECT '2' UNION SELECT '3' UNION
SELECT '4' UNION SELECT '5' UNION SELECT '6' UNION
SELECT '7' UNION SELECT '8' UNION SELECT '9' UNION SELECT '0') d2
) sub
WHERE i <= LENGTH(tab.col)-1
) sub
WHERE result <> 0
GROUP BY col;
Rextester Demo2
Get 4 character substring from beginning, convert implicitly to number, get number with lowest i.
Using locate(), least(), and substr()
SQLFiddle Demo
select col,SUBSTR(col,LEAST(
if (Locate(0,col) >0,Locate(0,col),999),
if (Locate(1,col) >0,Locate(1,col),999),
if (Locate(2,col) >0,Locate(2,col),999),
if (Locate(3,col) >0,Locate(3,col),999),
if (Locate(4,col) >0,Locate(4,col),999),
if (Locate(5,col) >0,Locate(5,col),999),
if (Locate(6,col) >0,Locate(6,col),999),
if (Locate(7,col) >0,Locate(7,col),999),
if (Locate(8,col) >0,Locate(8,col),999),
if (Locate(9,col) >0,Locate(9,col),999)
),4) as result from test;
Test Results:
mysql> create table test ( col varchar(15));
Query OK, 0 rows affected (0.70 sec)
mysql> insert into test (col) values
-> ('CC123484556'),
-> ('CC492014512'),
-> ('BUXT122256690');
Query OK, 3 rows affected (0.13 sec)
Records: 3 Duplicates: 0 Warnings: 0
Output:
mysql> select * from test;
+---------------+
| col |
+---------------+
| CC123484556 |
| CC492014512 |
| BUXT122256690 |
+---------------+
3 rows in set (0.00 sec)
mysql> select col,SUBSTR(col,LEAST(
-> if (Locate(0,col) >0,Locate(0,col),999),
-> if (Locate(1,col) >0,Locate(1,col),999),
-> if (Locate(2,col) >0,Locate(2,col),999),
-> if (Locate(3,col) >0,Locate(3,col),999),
-> if (Locate(4,col) >0,Locate(4,col),999),
-> if (Locate(5,col) >0,Locate(5,col),999),
-> if (Locate(6,col) >0,Locate(6,col),999),
-> if (Locate(7,col) >0,Locate(7,col),999),
-> if (Locate(8,col) >0,Locate(8,col),999),
-> if (Locate(9,col) >0,Locate(9,col),999)
-> ),4) as result from test;
+---------------+--------+
| col | result |
+---------------+--------+
| CC123484556 | 1234 |
| CC492014512 | 4920 |
| BUXT122256690 | 1222 |
+---------------+--------+
3 rows in set (0.00 sec)

How to select a primary key which has exact foreign keys matches a given list of values?

For example:
pk_ref fk
====== ===
1 a
1 b
1 c
2 a
2 b
2 d
How do I do a query like the "pseudo" query:
select distinc pk_ref
where fk in all('a', 'c');
The return query result must match all given values for the foreign key in the list.
The result should be:
1
While the following select must not return any records.
select distinc pk_ref
where fk in all('a', 'c', 'd');
How do I do that?
Try this
select pk_ref
from yourtable
group by pk_ref
having count(case when fk = 'a', then 1 end) >= 1
and count(case when fk = 'c' then 1 end) >= 1
To do it dynamically. (considering you are using SQL SERVER)
Create a split string function and pass the input as comma separated values
Declare #input varchar(8000)= 'a,c',#cnt int
set #cnt = len(#input)-len(replace(#input,',','')) + 1
select pk_ref
from yourtable
Where fk in (select split_values from udf_splitstring(#input , ','))
group by pk_ref
having count(Distinct fk) >= #cnt
You can create a split string function from the below link
https://sqlperformance.com/2012/07/t-sql-queries/split-strings
:list is the input list (bind variable). The difference of length() return values is the number of commas in the bind variable. This query, or something very close to it, should work in pretty much any DB product. Tested in Oracle.
select pk_ref
from tbl -- enter your table name here
where ',' || :list || ',' like '%,' || fk || ',%'
group by pk_ref
having count(distinct fk) = 1 + length(:list) - length(replace(:list, ',', ''))
If you can pass the IN operator values as Set, then you can do this as below
Schema:
SELECT * INTO #TAB FROM (
SELECT 1 ID, 'a' FK
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 1, 'c'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
UNION ALL
SELECT 2, 'd'
UNION ALL
SELECT 1, 'a'
)AS A
Used CTE to make 'a','c' as Set
;WITH CTE AS (
SELECT 'a' FK --Here 'a','c' passed as a Set through CTE
UNION
SELECT 'c'
)
,FINAL AS(
SELECT DENSE_RANK() OVER (PARTITION BY ID ORDER BY (FK))AS COUNT_ID, ID, FK
FROM #TAB where FK IN (select FK FROM CTE)
)
SELECT ID FROM FINAL WHERE COUNT_ID>=(SELECT COUNT( FK) FROM CTE)
Select pk_ref where fk='a' and pk_ref in (select pk_ref where fk='c' from yourtable) from yourtable;
or
select pk_ref where fk='a' from yourtable intersect select pk_ref where fk='c' from yourtable;
DECLARE #inputVariable VARCHAR(200) = 'a,b,c,d'
DECLARE #inputValue INT
DECLARE #tblInput TABLE
(
FK VARCHAR(100)
)
INSERT INTO #tblInput
SELECT SUBSTRING( #inputVariable+',',RN,1)
FROM (SELECT TOP 100 ROW_NUMBER() OVER(ORDER BY s.object_id) RN
FROM sys.objects s) s
where LEN(#inputVariable) >= RN
AND SUBSTRING(','+ #inputVariable,RN,1) = ','
SELECT #inputValue = COUNT(1) FROm #tblInput
--#inputVariable
DECLARE #tbl TABLE
(
ID INT,
FK VARCHAR(100)
)
INSERT INTO #tbl
SELECT 1 ID, 'a' FK
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 1, 'c'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
UNION ALL
SELECT 2, 'd'
UNION ALL
SELECT 1, 'a'
SELECT t.ID ,COUNT(DISTINCT t.FK)
FROM #tbl t
INNER JOIn #tblInput ti
ON t.FK = ti.FK
GROUP BY ID
HAVING COUNT(DISTINCT t.FK) = #inputValue

How to join 2 tables and order them?

I have 2 tables: _divions and _employee. In _employee there's a column division which is id_division in the table _division. I need to order them by the _division order first.
This table has a column order. I have tried this SQL statement, but it doesn't do much:
SELECT
e . *
FROM
`_employee` AS `e`
JOIN
`_division` AS d
ORDER BY
d.order,
e.division,
e.order
You missing ON clause. Try this
SELECT e.*
FROM _employee AS e
JOIN _division AS d ON d.id_division = e.division
// ^^^^ Missing
ORDER BY e.order
Below query may help. (Assuming there are divisions with no employees)
with employee as
(
select 'NISHA' ENAME, '10' EDIV FROM DUAL
UNION
SELECT 'ABC' ENAME, '10' EDIV FROM DUAL
UNION
SELECT 'DEF' ENAME, '15' EDIV FROM DUAL
UNION
SELECT 'GHI' ENAME, '20' EDIV FROM DUAL
),
DIVISION AS
(
SELECT '10' DIV_ID, 'INFO-TECH' DIV_DESC FROM DUAL
UNION
SELECT '15' DIV_ID, 'HUMAN RESOURCES' DIV_DESC FROM DUAL
UNION
SELECT '20' DIV_ID, 'SALES' DIV_DESC FROM DUAL
UNION
SELECT '25' DIV_ID, 'RESEARCH' DIV_DESC FROM DUAL
)
SELECT *
FROM EMPLOYEE, DIVISION WHERE
employee.EDIV (+) = DIVISION.DIV_ID
ORDER BY DIV_DESC, ENAME
If all employees would definitely have a division,then use employee.EDIV = DIVISION.DIV_ID instead of employee.EDIV (+) = DIVISION.DIV_ID

MySQL using variables in SELECT subquery

Is it possible to use variables in SELECT without SET before it?
Somehow '#period' variable not always works properly. For example in MySQL Workbench (v5.2), the result is showed only after 2nd invocation, if the variable is changed the result will be correct only on 2nd run. phpMyAdmin shows correct rows count but no entries, can someone explain please what is wrong in using variables in such a way:
select sql_no_cache #num := #num + 1 as `#`, l.* from (
select s.* from (
select
#num := 0 as 'Label 1',
#period := 31 as 'LAbel 2',
'' as 'Label 3'
union
select concat(users.first_name, ' ',users.last_name),u.type, u.date, u.description from (
select c.user_id as uguid, 'type1' as type, c.date_start as date, c.description from table1 c
where deleted = 0
and date_start > SUBDATE(CURRENT_DATE,#period)
union
...
) as u) as l
Found a mistake.
The reason is that the variable is used before it's definition. '#period' is defined in the query, but it's first usage occures in the subquery that's executes first.
So the solution is to put variable definition on the same level with the subquery where it used.
Something like:
select sql_no_cache #num := #num + 1 as `#`, l.* from (
select s.* from (
select
#num := 0 as 'Label 1',
'' as 'LAbel 2',
'' as 'Label 3'
union
select concat(users.first_name, ' ',users.last_name),u.type, u.date, u.description from (
select o2.* from
(select #period:=31) as o1
join (
select c.user_id as uguid, 'type1' as type, c.date_start as date, c.description from table1 c
where deleted = 0
and date_start > SUBDATE(CURRENT_DATE,#period)
union
...
) as o2
) as u
) as l