Flattening SQL tables into single CSV-friendly table - mysql

I'm taking over a database that needs to be presented in aCSV-friendly format for certain reasons.
Currently three tables exists. One with basic descriptions of an object, e.g.:
id | date | name
1 | 2008-10-10 | Maestro
2 | 2008-10-12 | Domo
Then I have another table containing event types and descriptions:
ev_id | desc | desc2
1 | Event1 | "Something less good happened"
2 | Event2 | "Something good happened"
Finally a third table making a connection between the two:
id | ev_id
1 | 2
2 | 1
What I need is to combine the three, such that each row in the event table produces a column in the final view together with the original object descriptions. If an event has occurred, a 1 should be inserted in the corresponding cell, otherwise 0 or NULL. E.g.:
id | date | name | Event1 | Event2
1 | 2008-10-10 | Maestro | 0 | 1
2 | 2008-10-12 | Domo | 1 | 0
Is this possible using SQL (MySQL)?

SELECT A.*,
(CASE WHEN B.ev_id = 1 THEN 1 ELSE 0 END) EVENT_1,
(CASE WHEN B.ev_id = 2 THEN 1 ELSE 0 END) EVENT_2,
.........
.........
(CASE WHEN B.ev_id = N THEN 1 ELSE 0 END) EVENT_N,
FROM table1 A JOIN table2 B
ON A.id = B.id
The only way to do this in mysql is to construct the query dynamicially by looping through the number of events in the events master table and then adding the same number of columns to the above query using dynamic sql and then execute.
SET #S = ' <construct the query> like the sample above ';
PREPARE n_StrSQL FROM #S;
EXECUTE n_StrSQL;
DEALLOCATE PREPARE n_StrSQL;
hope this helps

Related

How do i show the name of a column as a row data using a sql query?

I am quite inexperienced with the query writing.
I have a table like this,
+----+--------+------------+------------+------------+
| id | name | Character1 | Character2 | Character3 |
+----+--------+------------+------------+------------+
| 1 | A | 1 | 0 | 0 |
+----+--------+------------+------------+------------+
| 2 | B | 0 | 1 | 0 |
+----+--------+---------------+---------+------------+
i want to make a query where the result would show me like this, where i put a condition to search a name,
+----+--------+---------------+
| id | name | Character |
+----+--------+---------------+
| 1 | A | Character 1 |
+----+--------+---------------+
What will be the query for this?
There you go
select id, name, case 1
WHEN characters1 = 1 THEN characters1
WHEN characters2 = 1 THEN characters2
WHEN YourColumn = 1 THEN YourColumn //if you have more
Else 'no 1' END
from YourTableName where name LIKE '%searhname%'
Replace YourTableName above as your real table name, meanwhile searchname is what you or user is going to type in and search
YourColumn will be character2 and 3... or many more :)
For more information about LIKE, such as the difference between '%name' and 'name%', you may view this link here
You need a CASE expression:
select id, name,
case 1
when Character1 then 'Character1'
when Character2 then 'Character2'
when Character3 then 'Character3'
end Character
from tablename

MySQL recursive selection from one table

I have this table in MySQL:
| id | mainid | name |
+----+--------+---------------------+
| 1 | 0 | main 1 |
| 2 | 1 | sub 1 |
| 3 | 1 | sub 2 |
| 4 | 1 | sub 3 |
| 5 | 4 | subsub 1 |
| 6 | 4 | subsub 2 |
| 7 | 0 | main 2 |
| 8 | 7 | sub 4 |
| 9 | 7 | sub 5 |
The mainid field is associate with id field.
Is there a best practice in MySQL commands to select all row recursive? I want to select all subitems under main item.
I tried to select all subitems on first level for example sub 1, sub 2, sub3 is under main 1. This is simple:
SELECT id, mainid, name FROM mytable WHERE mainid = '1';
But is there a one-line-command to select same rows AND the subsub1 and subsub 2 rows too? (And of cours if I create another deeper levels thats too.)
you'll need temp tables and separate stored procedures
first stored procedure will receive the "parent" id and create a result temp table:
RESULT (id, mainid, name)
and a check temp table
CHECK (id, passed)
(this table is necessary to avoid infinite loops)
So, the idea is that you call the inner stored procedure, and the inner stored is something like this
PROC (currentId (int))
with the parent id and the proc will do basically what your query did, but save it in a inner temp table, and then for each element of that temp table (that is not in CHECK) it will mark it as passed in CHECK (just insert the row) and call the same proc for each of the "children" of currentId
Then insert all data from the inner temp table into RESULT and you'll have your entire list of descendants
you have 2 ways
check children and then insert into RESULT
or
insert into RESULT and then check children
the data will be ordered differently but the result should be the same

SELECT from Union x 3 using filter of another table

Background
I have a web application which must remove entries from other tables, filtered through a selection of 'tielists' from table 1 -> item_table 1, table 2, table 3.... now basically my result set is going to be filthy big unless I use a filter statement from another table, using a user_id... so can someone please help me structure my statement as needed? TY!
Tables
cars_belonging_to_user
-----------------------------
ID | user_id | make | model
----------------------------
1 | 1 | Toyota | Camry
2 | 1 |Infinity| Q55
3 | 1 | DMC | DeLorean
4 | 2 | Acura | RSX
Okay, Now the three 'tielists'
name:tielist_one
----------------------------
id | id_of_car | id_x | id_y|
1 | 1 | 12 | 22 |
2 | 2 | 23 | 32 |
-----------------------------
name:tielist_two
-------------------------------
id | id_of_car | id_x | id_z|
1 | 3 | 32 | 22 |
-----------------------------
name: tielist_three
id | id_of_car | id_x | id_a|
1 | 4 | 45 | 2 |
------------------------------
Result Set and Code
echo name_of_tielist_table
// I can structure if statements to echo result sets based upon the name
// Future Methodology: if car_id is in tielist_one, delete id_x from x_table, delete id_y from y_table...
// My output should be a double select base:
--SELECT * tielists from WHERE car_id is 1... output name of tielist... then
--SELECT * from specific_tielist where car_id is 1.....delete x_table, delete y_table...
Considering the list will be massive, and the tielist equally long, I must filter the results where car_id(id) = $variable && user_id = $id....
Side Notes
Only one car id will appear once in any single tielist..
This select statement MUST be filtered with user_id = $variable... (and remember, i'm looking for which car id too)
I MUST HAVE THE NAME of the tielist it comes from able to be echo'd into a variable...
I will only be looking for one single id_of_car at any given time, because this select will be contained in a foreach loop.
I was thinking a union all items would do the trick to select the row, but how can I get the name of the tielist the row is in, and how can the filter be used from the user_id row
If you want performance, I would suggest left outer join instead of union all. This will allow the query to make efficient use of indexes for your purpose.
Based on what you say, a car is in exactly one of the lists. This is important for this method to work. Here is the SQL:
select cu.*,
coalesce(tl1.id_x, tl2.id_x, tl3.id_x) as id_x,
tl1.y, tl2.idz, tl3.id_a,
(case when tl1.id is not null then 'One'
when tl2.id is not null then 'Two'
when tl3.id is not null then 'Three'
end) as TieList
from Cars_Belonging_To_User cu left ouer join
TieList_One tl1
on cu.id_of_car = tl1.id_of_car left outer join
TieList_Two tl2
on cu.id_of_car = tl2.id_of_car left outer join
TieList_Three tl3
on cu.id_of_car = tl3.id_of_car;
You can then add a where clause to filter as you need.
If you have an index on id_of_car for each tielist table, then the performance should be quite good. If the where clause uses an index on the first table, then the joins and where should all be using indexes, and the query will be quite fast.

MYSQL Function with a calcuation based on data in a db column

I Have a table that is a lookup for scoring points based on Place (P) and Number of Racers(R)
and scoring formats indicated by points_id. Two cases are shown in the table.
Sometime the points are determined directly by the values of P and N as in points_id =3
other times they are most easily determined by a simple calculation shown in the pts_calc column.
|points_id| P | N |points|pts_calc|
| 1 | 0 | 0 | NULL | pin |
| 1 |DNS| 0 | NULL | nin+1 |
| 3 | 1 | 0 |102.00| NULL |
| 3 | 2 | 0 | 98.00| NULL |
| 3 | 3 | 0 | 96.00| NULL |
| 3 | 4 | 0 | 93.00| NULL |
| 3 | 5 | 0 | 91.00| NULL |
| 3 | 6 | 0 | 89.00| NULL |
| 3 |DNF| 0 | 85.00| NULL |
I was hoping to create a function that returned the points from the three input variables.
points_id, P, N.
Below is what I tried.
CREATE FUNCTION POINTS(pid INT,pin VARCHAR(3),nin INT)
RETURNS DEC(6,2)
DETERMINISTIC
BEGIN
DECLARE pts DECIMAL(6,2);
DECLARE pcalc VARCHAR(20);
SELECT points,pts_calc INTO pts,pcalc FROM scoring_points WHERE points_id=pid AND (P=pin OR P='0') AND (N=nin or N=0);
IF(pts IS NULL) THEN
SET #s= CONCAT('SET pts = ',pcalc);
PREPARE stmt FROM #s;
EXECUTE stmt;
END IF;
RETURN pts;
END
But i got this error.
1336 - Dynamic SQL is not allowed in stored function or trigger
Further research show the Prepare statement is not allowed in functions only but procedures.
I was hoping to do something like;
SELECT SUM(Points(pid,place,numb)) FROM t1 GROUP BY racer.id
But onto plan B (tbd) unless someone has great idea.
I think you might fare better having three numeric columns instead of your pts_calc column:
cPIN - coefficient of pin term
cNIN - coefficient of nin term
cnst - constant term
Your function could then perform:
SELECT IFNULL(points, cPIN*pin + cNIN*nin + cnst) INTO pts
FROM scoring_points
WHERE ...
Depending on your needs, you might even be able to get rid of the points column by just using cnst and leaving the other two equal to 0.

Find & Sort By Column Name in MYSQL Given Condition

In a table I have the following value:
ID | Exercise1 | Exercise2 | Exercise3
1 | 0 | 0 | 0
2 | 0 | 0 | 0
When a user completes an exercise, the db switches from '0' to '1'. I'm looking for an sql query that searches by the ID number of the user returns the lowest column name that is set to 0.
EX:
ID | Exercise1 | Exercise2 | Exercise3
1 | 1 | 1 | 0
Here the query would return with exercise3, since exercise1 and exercise2 have previously been updated and completed by the user.
I found
SELECT COLUMN_NAME FROM information_schema.columns
but can't put it together with the sorting I'm looking for, any help would be deeply appreciated.
If you have only a handful of exercises (e.g. < 5), then you can simply hardcode the query with a series of nested IF() statements.
If you have more than that, then you should change your data model so each user/exercise mapping is stored in a separate row.
Something like this?
SELECT CASE
WHEN Exercise1=0 THEN 'Exercise1'
WHEN Exercise2=0 THEN 'Exercise2'
WHEN Exercise3=0 THEN 'Exercise3'
ELSE NULL
END AS Exercise
FROM MyTable
WHERE ID = SomeID
Hmmm... you have problems because your design is wrong. The problem is that your database design was affected by how you imagine the presentation of the table. But the database thinking is different. The database would be normally designed this way:
StudentID | ExcerciseID | Completed
1 | 1 | 1
1 | 2 | 1
1 | 3 | 0
2 | 1 | 0
....
And then you can do:
select StudentID, min(ExcerciseID) as FirstExcerciseNotCompleted
from Excercises
where Completed = 0
to see first incomplete excercise for each student, or if you want set next completed excercise to Student 1, just do:
update Excercises
set Completed = 1
where Student = 1 and ExcerciseID = (select min(ExcerciseID) from Excercises where StudentID = 1 and Completed = 0)