MySql Disorder Result set using query - mysql

Good Evening,
I have a query like this:
SELECT #c:=#c+1 as Count, CurrentISP
FROM (
SELECT 'yahoo.com' as currentISP
UNION ALL SELECT 'yahoo.com'
UNION ALL SELECT 'gmail.com'
UNION ALL SELECT 'gmail.com'
UNION ALL SELECT 'hotmail.com'
UNION ALL SELECT 'hotmail.com'
) t
INNER JOIN ( SELECT #c:=0 ) c
Which produces a result set as follows:
Count CurrentISP
--
1 yahoo.com
2 yahoo.com
3 gmail.com
4 gmail.com
5 hotmail.com
6 hotmail.com
What I want to do is give this set an Ordering as follow:
1) yahoo.com
2) gmail.com
3) hotmail.com
4) maybe some ither Email Provider
5) when All providers are over start over again
6) yahoo.com etc etc
The reason I would like to do this, is because is to avoid spamming a certain provider with emails at the same time... So I can increase my sender score reputation back to 100%.

This is a little hacky but you can order by a count for each time the name appears.. this will loop for as many records as you put in.
SELECT #D:=#D+1 AS Count, CurrentISP
FROM
( SELECT #C:=0, #A:=0, #B:=0, #D:=0 ) temp,
(
SELECT *
FROM (
SELECT 'yahoo.com' AS currentISP
UNION ALL SELECT 'yahoo.com'
UNION ALL SELECT 'gmail.com'
UNION ALL SELECT 'gmail.com'
UNION ALL SELECT 'hotmail.com'
UNION ALL SELECT 'hotmail.com'
) t
ORDER BY
CASE
WHEN currentISP='yahoo.com' THEN #A := #A + 1
WHEN currentISP='gmail.com' THEN #B := #B + 1
WHEN currentISP='hotmail.com' THEN #C := #C + 1
END DESC
) AS t
this is what the query returns: IMAGE

If you want to order a list of duplicate values, maybe something like this would be better
select currentIsp
from
(
select 1 as n
union select 2
-- Add as many unique numbers you need
) as a,
(
select 'yahoo.com' as currentISP
union select 'gmail.com'
union select 'hotmail.com'
-- More UNION queries with unique ISP names
) as t
order by a.n, t.currentIsp
(I don't think you need a Count column, but you can add it if you want)

Related

Add the result of SELECT query in another SELECT query

This is the result of a UNION of two SELECT
SELECT count(*) FROM
((SELECT session_id_current_user from test.tws_analytics
WHERE (add_date BETWEEN '2022-05-15' AND '2022-05-15') AND ((pathURL='vues/login.php' AND name_current_user='') OR (pathURL='' AND searchURL='?job=forgotten' AND name_current_user=''))
AND session_id_current_user NOT IN
(SELECT session_id_current_user from test.tws_analytics
WHERE (pathURL <> 'vues/login.php' AND searchURL <> '?job=forgotten') AND add_date BETWEEN '2022-05-15' AND '2022-05-15' order by session_id_current_user)
order by session_id_current_user)
UNION
(SELECT name_current_user from test.tws_analytics where add_date BETWEEN '2022-05-15' AND '2022-05-15' AND name_current_user IS NOT NULL AND name_current_user <> ''))
AS tem
The result is 11.
What I want to do is to select this result with other columns like this :
SELECT count(session),count(name), [AND tem.count(*)] FROM ....
This is the general idea, though i didn't know how to implement it.
a simplified general answer would be
select * from (select count(*) numsessions from sessions), (select count(*) numusers from users)
this will give 2 different counts, i didn't include the logics that you provided, but that will need to be done inside the 2 subqueries.

SQL merge different table data counts

Below is my query to get some data for dashboard screen.
SELECT COUNT(*) as occupied_rooms FROM rooms where available='N' ;
SELECT COUNT(*) as checkedIn_guests FROM booking where checkout_time='' ;
SELECT COUNT(*) as available_rooms FROM rooms where available='Y' ;
SELECT COUNT(*) as total_guest FROM booking;
SELECT COUNT(*) as total_booking FROM booking;
SELECT COUNT(*) as total_staff FROM employee;
It produce output as show in above
How ever I want the output like given above image.
Use union all
SELECT 'occupied_rooms' as which, COUNT(*) as cnt FROM rooms where available = 'N'
UNION ALL
SELECT 'checkedIn_guests', COUNT(*) FROM booking where checkout_time = ''
UNION ALL
SELECT 'available_rooms', COUNT(*) FROM rooms where available = 'Y' ;
UNION ALL
SELECT 'total_guest', COUNT(*) FROM booking;
UNION ALL
SELECT 'total_booking', COUNT(*) FROM booking;
UNION ALL
SELECT 'total_staff', COUNT(*) FROM employee;
You could make the query a bit more efficient by combining the queries that reference the same table. But the overall structure would be essentially the same.
You could use a UNION for build single table result with each result in a row
SELECT 'occupied_rooms', COUNT(*) count
FROM rooms
where available='N'
UNION
SELECT 'checkedIn_guests', COUNT(*)
FROM booking
where checkout_time=''
UNION
SELECT 'available_rooms', COUNT(*)
FROM rooms
where available='Y'
UNION
SELECT 'total_guest', COUNT(*)
FROM booking
UNION
SELECT 'total_booking', COUNT(*)
FROM booking
UNION
SELECT 'total_staff', COUNT(*)
FROM employee;

How to insert a row in mysql before union?

Say I have the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
)
) AS atable
How can I make it such that I can "stick in a new row at the beginning of the table" prior to the SELECT union such that it would start with:
rownum | name| age
1 | "Jordan" | 6 <-- This is an arbitrarily inserted record with name="Jordan" age="6" that is not a part of any of the clubAmembers or clubBmembers table.
The rest of the table (rownum 2 and onwards) would contain the actual result form the union with clubAmembers then clubBmembers.
Basically I am looking for:
CREATE TABLE
INSERT a row "Jordan" | 6
Perform select with union such that the rows after the first would start with "rownum=2", all the data from clubAmembers, etc.
How to best do this?
"At the beginning of the table" is not truly meaningful to relational databases because the order results are returned are not guaranteed until you use an ORDER BY clause, at which point the order on disk becomes a moot point anyway.
In your case, since you want to guarantee an order in your result clause (and therefore ordering #rownum, you will have to use ORDER BY. Something like:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION
SELECT name, age, 1 AS ord FROM clubAmembers
UNION
SELECT name, age, 1 AS ord FROM clubBmembers
ORDER BY ord
)
) AS atable
Note that at no point does this guarantee that rows in clubAmembers will have a lower rownum than rows in clubBmembers. If you want to guarantee that clubAmembers have a lower rownum, while keeping the semantics of UNION (versus UNION ALL), you can use the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
ORDER BY ord
)
) AS atable
Note if {name, age} could be duplicated within the clubXmembers table, you will need to add DISTINCT:
...
SELECT DISTINCT name, age, 1 AS ord FROM clubAmembers
UNION ALL
...
As per the request in the comments, if you had a clubCmembers table, you would do:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
SELECT name, age, 3 AS ord FROM clubCmembers AS c
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = c.name AND a.age = c.age)
AND NOT EXISTS(SELECT 1 FROM clubBmembers AS b
WHERE b.name = c.name AND b.age = c.age)
ORDER BY ord
)
) AS atable
I'm not sure if I got it right. But why don't you just add another union like this:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT 1, "Jordan", 6
UNION ALL
SELECT name, age FROM clubAmembers
UNION ALL
SELECT name, age FROM clubBmembers
)
) AS atable
You can separate the create table statmenet from the insert statmenet:
Create the table (you must know which colums are gona be there)
Insert your 1st record (INSERT INTO .... Values(...))
Use your statement but with insert into instead of create table like: INSERT INTO YourNewTable.... Values(YourSubQuery) (Nr and tye of columns must match your subquery)
This should do, I believe:
CREATE TABLE newtable AS (
SELECT (#rownum:=IFNULL(#rownum,0)+1)+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
) AS s
UNION ALL
SELECT 1, 'Jordan', 6
) AS atable
Demo at SQL Fiddle: http://sqlfiddle.com/#!2/ab825/6

MySQL; Get information from 3 table same time

People
here is my little problem.
I have three table:
a_names_1
b_names_2
c_names_3
they are same by structure. all of them has two item: name and used
Is there any QUERY to run to get and count all the 'name' that has 'used'=1 from all those three tables together.
I've tried this one, but didn't work:
(SELECT COUNT(*) 'name' from a_names_1) UNION
(SELECT COUNT(*) 'name' from a_names_2) UNION
(SELECT COUNT(*) 'name' from a_names_3) WHERE `used`=1
I'm using PHPMyAdmin for MySQL.
Any Help would be appreciated.. thanks in advance
This query outputs count of distinct names from all tables with used=1
select count(distinct name)
from
(
select name,used from a_names_1 where used=1
union all
select name,used from a_names_2 where used=1
union all
select name,used from a_names_3 where used=1
) t
If you need to SUM all USED for each NAME from all tables and output only with SUM of used=1 then:
select count(*) from
(
select name, SUM(used)
from
(
select name,used from a_names_1
union all
select name,used from a_names_2
union all
select name,used from a_names_3
) t
GROUP BY name
HAVING SUM(used)=1
) t1
select count(*) as name
from
(
select name, used from a_names_1
union
select name, used from a_names_2
union
select name, used from a_names_3) t
where t.used = 1
Probably this is slow, because you lose the index optimizations. What I would do is do the three queries, something like
SELECT SUM('name') AS name_sum
FROM ((SELECT COUNT(*) 'name' from a_names_1 WHERE `used`=1)
UNION (SELECT COUNT(*) 'name' from a_names_2 WHERE `used`=1));
If this doesn't work, it is probably a problem with the usage of name
Maybe you wanted this way:
select count(*) as cnt
from
(
select name from a_names_1 t1 where t1.used = 1
union
select name from a_names_2 t2 where t2.used = 1
union
select name from a_names_3 t3 where t3.used = 1
) t
The straight forward solution;
SELECT SUM(used) FROM (
SELECT used FROM a_names_1 WHERE used=1
UNION ALL
SELECT used FROM a_names_2 WHERE used=1
UNION ALL
SELECT used FROM a_names_3 WHERE used=1
) a
SQLfiddle for testing
An alternative if you have an index on used (and the only values of used are 0 or 1) is to just do the counting using the index;
SELECT SUM(used) total FROM (
SELECT SUM(used) used FROM a_names_1
UNION ALL
SELECT SUM(used) FROM a_names_2
UNION ALL
SELECT SUM(used) FROM a_names_3
) a
SQLfiddle for this example.
If you look at the query plan of the latter query, you can see it uses the indexes effectively.

SELECT de-normalized columns into separate records?

I am playing around with SQL a little just so I am not completely ignorant about it if I am ever asked in a job interview. My friend was recently asked the following question at an interview and he couldn't get it and I asked somebody at work who knows SQL decently and he didn't know. Can you guys answer this problem for me and then explain how it works? Please?
*The problem*
Database normalization (or lack of normalization) often presents a challenge for developers.
Consider a database table of employees that contains three fields:
EmployeeID
EmployeeName
EmailAddresses
Every employee, identified by a unique EmployeeID, may have one or more comma-separated, #rockauto.com email address(es) in the EmailAddresses field.
The database table is defined below:
CREATE TABLE Employees
(
EmployeeID int UNSIGNED NOT NULL PRIMARY KEY,
EmployeeName varchar(50) NOT NULL,
EmailAddresses varchar(40) NOT NULL ,
PRIMARY KEY(EmployeeID)
);
For testing purposes, here is some sample data:
INSERT INTO Employees (EmployeeID, EmployeeName, EmailAddresses) VALUES
('1', 'Bill', 'bill#companyx.com'),
('2', 'Fred', 'fred#companyx.com,freddie#companyx.com'),
('3', 'Fred', 'fredsmith#companyx.com'),
('4', 'Joe', 'joe#companyx.com,joe_smith#companyx.com');
Your task is to write a single MySQL SELECT query that will show the following output for the sample data above:
Employee EmailAddress
Bill bill#companyx.com
Fred (2) fred#companyx.com
Fred (2) freddie#companyx.com
Fred (3) fredsmith#companyx.com
Joe joe#companyx.com
Joe joe_smith#companyx.com
Please take note that because there is more than one person with the same name (in this case, "Fred"), the EmployeeID is included in parenthesis.
Your query is required to written in MySQL version 5.1.41 compatible syntax. You should assume that the ordering is accomplished using standard database ascending ordering: "ORDER BY EmployeeID ASC"
For this problem, you need to submit a single SQL SELECT query. Your query should be able to process a table of 1000 records in a reasonable amount of time.
only if you have less than 10000 emails.... is that acceptable?
select
if(t1.c > 1, concat(e.employeename, ' (', e.employeeid, ')'), e.employeename) as Employee,
replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') EmailAddress
from
(select employeename, count(*) as c from Employees group by employeename) as t1,
(select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
(SELECT #row := #row + 1 as row FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x2,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x3,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x4,
(SELECT #row:=0) as ff) as n,
Employees e
where
e.employeename = t1.employeename and
e.employeeid = t2.employeeid and
n.row <= t2.emails
order by e.employeeid;
EDIT:
With less useless numbers generated:
select
if(t1.c > 1, concat(e.EmployeeName, ' (', e.EmployeeID, ')'), e.EmployeeName) as Employee,
replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') as EmailAddress
from
(select EmployeeName, count(*) as c from Employees group by EmployeeName) as t1,
(select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
(select `1` as row from (select 1 union all select 2 union all select 3 union all select 4) x) as n,
Employees e
where
e.EmployeeName = t1.EmployeeName and
e.EmployeeID = t2.EmployeeID and
n.row <= t2.emails
order by e.EmployeeID;
And what did we learn? Poor database design results awful queries. And you can do stuff with SQL, that are probably supported only because people do poor database designs... :)