I have a situation which deals with two sets of data with different columns. I know I could use UNION but UNION requires equal number of expressions in both tables. I am trying to join these two statements in my stored procedure. The first statement has one Extra Column 'Location'
Select
TableA.Name,
TableB.Occupation,
TableA.Location,
'Group1' AS [groupBy]
From
TableA,
TableB
Where
TableA.ID = 1
Select
TableA.Name,
TableB.Occupation,
'Group2' AS [groupBy]
From
TableA,
TableB
Where
TableB.ID = 10
My result should look like this
Name Occupation GroupBy Location
David Doctor Group1 USA
John Pilot Group1 Asia
Dwayne Wrestler Group2 NULL
Axel RockStar Group2 NULL
My Table structure
Table A
ID Name Occupation Location
1 David Doctor USA
1 John Pilot Asia
2 Mike Clerk Europe
Table B
ID Name Occupation
3 Wayne Writer
4 Shane Publisher
10 Dwayne Wrestler
10 Axel Rockstar
That's called a UNION; just manually add ", NULL" to the select list in the second query
Add as many of them as you need, whereever you need them. If you need additional columns in both tables, instances of NULL in the first table must be aliasd with the appropriate column name.
Related
I have two tables
1. studentprofile
sud_id name
1 kp
2 kishan
3 raj
2. fee_generate
fee_id stud_id fee_balance name
1 1 0 kp
2 2 10 kishan
I want to show those students whose fee is not submitted or have any balance, which means that I want to show is as following
kishan and raj
I am not able to write the query with any join. My second problem is that name columns are common in both tables.
So the selected name column should come from studentprofile table and order by name from studentprofile table.
You need to left join the fee_generate table on the student_profile table and include those records where the fee_generate.student_id is null (not submitted) or the balance is over 0:
select s.* from student_profile s
left join fee_generate f on s.stud_id=f.stud_id
where f.stud_id is null or f.balance>0
order by f.name
ID Name FatherID Birthyear
1 Bart NULL 1756
2 Franz 1 1796
3 Josef 2 1835
4 Zohan 3 1887
Suppose I have this table, I would like to know if Zohan is the son of Bart, which can be gotten if I compare the values from the column "FatherID" with the ID of the previous rows up until I get to Bart. But how do I compare the values in the same table but of different rows and columns
You could self join the table:
SELECT s.name AS son_name, f.name AS father_name
FROM mytable s
JOIN mytable f ON s.fatherID = f.id
-- possibly add a where clause with conditions on son/father names
I need to create a digest of records which are grouped by names in a table. The problem is, my table has two columns for names and the same names can be in either column. The digest should list all names and the records in which they exist, regardless of which column.
I can see doing this by first generating a unique list of names from both columns, then scanning through the result to see if the names appear in either column. I want to know if MySQL can do this in one query.
Example, here's my table:
Id ColumnA Column B
1 Bill NULL
2 NULL Dennis
3 Adam Carl
4 NULL Adam
5 Adam Bill
6 Dennis NULL
7 Frank Bill
The result of the query (NULL sorts to the top)
NULL 4 NULL Adam
NULL 1 Bill NULL
NULL 2 NULL Dennis
NULL 6 Dennis NULL
Adam 5 Adam Bill
Adam 7 Adam Carl
Bill 1 Bill NULL
Bill 5 Adam Bill
Bill 6 Frank Bill
Carl 3 Adam Carl
Dennis 2 NULL Dennis
Dennis 6 Dennis NULL
Frank 7 Frank Bill
It would be nice if I could eliminate the NULL entries at the top of the list. I only put them there because everything I'm trying so far has them at the top. I'd rather the result looks like this:
Adam 5 Adam Bill
Adam 7 Adam Carl
Bill 1 Bill NULL
Bill 5 Adam Bill
Bill 6 Frank Bill
Carl 3 Adam Carl
Dennis 2 NULL Dennis
Dennis 6 Dennis NULL
Frank 7 Frank Bill
You need to do a union of ID, ColumnA with ID, ColumnB, then summarize and order the result.
Like so http://sqlfiddle.com/#!2/eeec6/5/0:
SELECT name, GROUP_CONCAT(DISTINCT id ORDER BY id) AS ids
FROM (
SELECT id, a AS name
FROM names
UNION
SELECT id, b AS name
FROM names
) AS t
WHERE name IS NOT NULL
GROUP BY name
ORDER BY name
This will give you one row per name, with a list of IDs (row numbers) containing that name.
Or you can simply display the name, id columns one at a time(http://sqlfiddle.com/#!2/eeec6/6/0):
SELECT name, id
FROM (
SELECT id, a AS name
FROM names
UNION
SELECT id, b AS name
FROM names
) AS t
WHERE name IS NOT NULL
ORDER BY name, id
Finally, if you want to show the whole row with each name, you can do this (http://sqlfiddle.com/#!2/eeec6/8/0):
SELECT name, n.id, n.a, n.b
FROM (
SELECT id, a AS name
FROM names
UNION
SELECT id, b AS name
FROM names
) AS t
JOIN names AS n ON t.id = n.id
WHERE name IS NOT NULL
ORDER BY name, n.id
One approach:
SELECT s.ColumnA AS digest_name
, s.ID
, s.ColumnA
, s.ColumnB
FROM mydata s
WHERE s.ColumnA IS NOT NULL
UNION ALL
SELECT t.ColumnB
, t.ID
, t.ColumnA
, t.ColumnB
FROM mydata t
WHERE t.ColumnB IS NOT NULL
ORDER BY 1, 3, 4
One query gets all the rows where ColumnA is not null, the second query gets the rows where Column B is not null. (This effectively omits rows where the digest name would have been NULL.)
The UNION ALL set operator combines the two sets of rows into a single set. (NOTE: We prefer the more efficient UNION ALL operator over the UNION operator, because it doesn't perform the extra work of identifying and removing duplicate rows, when that operation isn't required.)
The ORDER BY specifies that the rows are to be ordered by the digest name first, then by the names in ColumnA and ColumnB.
This produces ten rows of output, and includes row:
digest Id ColumnA ColumnB
-------- -- -------- --------
Adam 4 NULL Adam
It's not clear why this row is omitted from the resultset specified in the question. (That resultset in the question shows only nine rows.)
So, I'm not sure if I'm missing something about the requirement. It's entirely possible I've not understood the specification.
I have the following tables in my database:
Table: Employee
ID Name
-- ----
1 Mike
2 Peter
3 Daniel
Table: Location
EmployeeID City
---------- ----
1 Berlin
1 Stuttgart
1 München
2 Hamburg
3 Stuttgart
3 Berlin
The Employee table contains information about the employees. The Location table contains information about the locations the employees have their projects in (e.g Mike has projects in Berlin, Stuttgart, and München).
What I want to do now is to filter employees by given locations in an array but still retrieve all the locations of each employee. For example, filtering by the array ["Berlin", Stuttgart] should return the following result:
ID Name City
-- ---- ----
1 Mike Berlin, Stuttgart, München
3 Daniel Stuttgart, Berlin
I know that I can use GROUP_CONCAT() to concatenate the locations. But how can I do the filtering? I need to be able to do it using SQL.
Apply WHERE condition to get EmployeeID from Location table:
SELECT
Employee.*,
GROUP_CONCAT(Location.City)
FROM
Employee
INNER JOIN Location
ON Employee.ID=Location.EmployeeID
INNER JOIN
(SELECT DISTINCT
EmployeeID
FROM
Location
WHERE
City IN ('Berlin', 'Stuttgart')) AS IDS
ON Employee.ID=IDS.EmployeeID
GROUP BY
Employee.ID
-check the fiddle.
Alternative solution would be to use HAVING clause with plain query and INSTR(), for example, but I don't recommend you to do it because comparing in HAVING will be slow and, besides, list of values will produce multiple comparisons.
You can use below query:
SELECT
a.id,a.`name`,GROUP_CONCAT(b.city)
FROM employee a
JOIN
(SELECT DISTINCT employeeid,city FROM location WHERE location IN ('Berlin', 'Stuttgart')) b
ON a.id=b.employeeid
GROUP BY a.id;
I have a table with students names, auto increased NR record, but on each edit of the record, a new one is created copying the NR to the ID field.
But when I try to group the ID records when MAX(NR) it shows me the max number of that ID but when I ask for the remaining of the rocord, it doesn't show me that last record of that group of ID's in
SELECT MAX(`NR`) AS 'mNr',`NR`,`ID`,`Name1`,`Name3`,`Gender`
FROM `Kids` GROUP BY `ID`
This produces results like:
mNr NR ID Name1 Name3 Gender
252 1 1 Alice Carper f
179 2 2 Dorah Fisher f
189 3 3 Racheal King f
173 4 4 Frank Smith m
192 5 5 Patrick Fay m
305 6 6 Gloria Sing f
299 7 7 Bridget Young f
But as you can see, the query shows the highest edit NR, but then continues to give the lowest of the rest of the record, not the record details belonging to that lastest NR...
What am I doing wrong?
This is the sample data:
NR ID Name1 Name3 Gender
1 1 Alice Achand f
2 2 Dorah Achieng f
3 3 Racheal Achieng f
4 4 Francisca Adikin f
5 5 Patrick Adilu m
6 6 Gloria Ajwang f
7 7 Bridget Aketch f
130 5 Patrick Adilu m
129 4 Francisca Adikin f
128 2 Dorah Achieng f
153 4 Francisca Adikin f
173 4 Francisca Adikin f
179 2 Dorah Achieng f
189 3 Racheal Achieng f
192 5 Patrick Adilu m
252 1 Alice Wor f
299 7 Bridget Aketch f
305 6 Gloria Ajwang f
Perhaps without knowing it, you are using an arcane feature of MySQL. MySQL allows you to include columns in the select statement of an aggregation query that are not in aggregation functions or in the group by clause. The engine puts in arbitrary values for these columns.
The correct way to do what you want is with a join:
SELECT k.*
FROM `Kids` k join
(select id, max(nr) as maxnr
from kids
group by id
) m
on k.id = m.id and nr = maxnr;
Here is the explicit explanation in the documentation:
MySQL extends the use of GROUP BY so that the select list can refer to
nonaggregated columns not named in the GROUP BY clause. This means
that the preceding query is legal in MySQL. You can use this feature
to get better performance by avoiding unnecessary column sorting and
grouping. However, this is useful primarily when all values in each
nonaggregated column not named in the GROUP BY are the same for each
group. The server is free to choose any value from each group, so
unless they are the same, the values chosen are indeterminate.
Furthermore, the selection of values from each group cannot be
influenced by adding an ORDER BY clause. Sorting of the result set
occurs after values have been chosen, and ORDER BY does not affect
which values within each group the server chooses.
which you can read in more detail here.
Although #Gordon Linoff's answer is technically correct, using sub-queries may be more resource and time consuming on large data sets.
Depending on the case, I would usually separate the data into two tables students and student_details where
students's table structure is
CREATE TABLE students (
student_id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT
);
The idea of this table is to create a unique number for the student and store any other student data that you may not want to save in the revisions.
student_details's table structure is:
CREATE TABLE student_details (
revision_id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
student_id INTEGER NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
gender VARCHAR(1),
is_history BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT FOREIGN KEY(student_id) REFERENCES students(student_id) ON DELETE RESTRICT
);
And this table stores the actual data of the student. When the data for a certain student is updated, you just need to update the is_history column to true for the records of that student. Then, when selecting your student data, you'd simply use SELECT student_details.* FROM students LEFT JOIN student_details ON (student_details.student_id = students.student_id AND student_details.is_history = false). This will always return the latest revision of a student's detail.
Inserting a new student
Insert a student INSERT INTO students(student_id) VALUES('');
Get the lasted insert id SELECT LAST_INSERT_ID(); (let's say it returns 1 for the sake of this example)
Insert the student details INSERT INTO student_details(student_id, first_name, last_name, gender) VALUES('1', 'Alice', 'Carper', 'f')
Updating an existing student (let's say student_id = 1)
Set all previous student_detail "revisions" as "history": UPDATE student_details SET is_history = true WHERE student_details.student_id = 1 AND is_history = false
Add the new revision: INSERT INTO student_details(student_id, first_name, last_name, gender) VALUES('1', 'Alice', 'Achand', 'f')
Getting a student and his latest student details
SELECT student_details.* FROM students LEFT JOIN student_details ON (student_details.student_id = students.student_id AND student_details.is_history = false)