Related
I have table with following information
id | order_id | batch_id | bucket_id | menu_id | product_id | type_id | size
1 | 1 | 1 | 1 | 1 | 1 | 1 | small
2 | 1 | 1 | 1 | 1 | 5 | 1 | small
3 | 1 | 1 | 1 | 1 | 5 | 1 | medium
I want to achieve following
order_id | batch_id | product1 | product5
1 | 1 | 1 x small| 1 x small, 1 medium
Is this possible to write a query to achieve this?
It's possible in MySQL using this kind of query:
SELECT order_id, batch_id,
GROUP_CONCAT(CASE WHEN product_id=1 THEN CONCAT(type_id,' x ', size) END) AS product1,
GROUP_CONCAT(CASE WHEN product_id=5 THEN CONCAT(type_id,' x ', size) END) AS product5
FROM table1
GROUP BY order_id, batch_id
The problem with this is that it's not dynamic so if you have hundreds, thousands of products, the query will be very hard to maintain. One possible solution in MySQL is using prepared statement. Here is an updated example after #ggordon spotted that my previous attempt show duplicates:
SET #columns := (SELECT GROUP_CONCAT(CONCAT("GROUP_CONCAT(CASE WHEN product_id=",product_id,"
THEN CONCAT(cnt,' x ', size) END)
AS product",product_id,"
"))
FROM (SELECT DISTINCT product_id FROM table1) t1);
SET #query := CONCAT('SELECT order_id, batch_id, ',#columns,'
FROM (SELECT product_id, order_id, batch_id, size, COUNT(*) cnt
FROM table1 GROUP BY product_id, order_id, batch_id, size) t1
GROUP BY order_id, batch_id');
PREPARE stmt FROM #query ;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
There are 2 variables being used in there and I named each variable to represent what is it (hopefully).
Demo fiddle
The following query would return the desired data you would need
SELECT
order_id,
batch_id,
product_id,
concat(
count(product_id),
' x ',
size
) as size_cnt
FROM
t1
GROUP BY
order_id,
batch_id,
product_id,
size;
order_id
batch_id
product_id
size_cnt
1
1
1
1 x small
1
1
5
1 x small
1
1
5
1 x medium
View working demo on DB Fiddle
However, in order to get it in the desired format, you would need to pivot the data. You could achieve this with the assistance of group_concat as shown in the sql example below:
SELECT
order_id,
batch_id,
-- we can generate from here
REPLACE(
GROUP_CONCAT(
',',
CASE
WHEN product_id=1 THEN size_cnt
END
),
',,',
','
) as product1,
REPLACE(
GROUP_CONCAT(
',',
CASE
WHEN product_id=5 THEN size_cnt
END
),
',,',
','
) as product5
-- to here. See explanation below
FROM (
SELECT
order_id,
batch_id,
product_id,
concat(
count(product_id),
' x ',
size
) as size_cnt
FROM
t1
GROUP BY
order_id,
batch_id,
product_id,
size
) t2
GROUP BY
order_id,
batch_id
order_id
batch_id
product1
product5
1
1
,1 x small
,1 x small,1 x medium
View working demo on DB Fiddle
However, as you can see, you would have to know the product_ids before hand for the desired columns.
If you are uncertain about the product ids that you will have, writing a dynamic query would be helpful here. You could start by getting all the product_ids.
I'm using the DB facade from Laravel here, however, you may use the Eloquent ORM or other methods to achieve the following:
//I have a collection of product ids i.e. `collect([1,5])` based on your example
$productIds = DB::select("select distinct product_id from t1")
->pluck('product_id');
Then generating a dynamic sql query to run on your table
$productExpression = DB::select("select distinct product_id from t1")
->pluck('product_id')
//for each product id let us return an sql expression
->map(function($productId){
return "
REPLACE(
GROUP_CONCAT(
',',
CASE
WHEN product_id=$productId THEN size_cnt
END
),
',,',
','
) as product$productId
";
})
//combine the expressions into one string
->join(",");
We can now create a combined query as
$combinedQuery="
SELECT
order_id,
batch_id,
$productExpression
FROM (
SELECT
order_id,
batch_id,
product_id,
concat(
count(product_id),
' x ',
size
) as size_cnt
FROM
t1
GROUP BY
order_id,
batch_id,
product_id,
size
) t2
GROUP BY
order_id,
batch_id;
";
//running the query to retrieve the results
$results = DB::select($combinedQuery);
Let me know if this works for you.
If you are using php with either PDO or mysqli you can get PHP to concat the fields.
$result = $db->query("SELECT * FROM TABLE");
while($row = $result->fetch_assoc()) {
//do stuff with data
$product .= $row["product_id"] . " x " . $row["size"].", ";
}
I am preparing a query in which the total to be paid is added to the number of daily libraries that a student requests throughout the month.
It is worth mentioning that there are several types of libraries and what I need is to be able to associate the document payable to the game library catch of that month depending on the type of library
So, if a student requested 5 libraries in the month and 2 are morning and 3 are sports. 2 documents payable would be created and the morning document payable would be assigned to the 2 captures and the sports document to the 3 captures of the month.
CREATE PROCEDURE pagodeludotecaalumnos (IN alumnoid, IN fecha) BEGIN
insert into cj_documentoporpagar(documentoid, subconceptoid, pagoestatusid, alumnoid, cicloid, gradoid,
mediopagoid, importe, saldo, fechalimitepago, fechacreacion, fechaprontopago, referencia, documento,
hermanos, reingreso, padreexalumno, concepto, iva)
select 17 as DocumentoId,
case when a.TipoId = 1 or a.tipoid = 2 then 122
when a.TipoId = 3 then 235
when a.TipoId = 4 then -1 end as SubConceptoId,
1 as PagoEstatusId, b.AlumnoId,
5 as CicloId, b.GradoId, 1 as MedioPagoId,
case when c.PrimerNombre like '%*%' or c.ApellidoPaterno like '%*%' or c.ApellidoMaterno like '%*%' then 40 * Count(*) else 55 * Count(*) end as Importe,
case when c.PrimerNombre like '%*%' or c.ApellidoPaterno like '%*%' or c.ApellidoMaterno like '%*%' then 40 * Count(*) else 55 * Count(*) end as Saldo,
DATE(DATE_ADD(LAST_DAY(fecha), INTERVAL 1 MONTH)) as FechaLImitePago, now() as FechaCreacion, now() as FechaProntoPago,
'' as Referencia, '202003L' as documento, 0 as Hermanos, 0 as Reingreso, 0 as PadreExAlumno,
Concat('Ludoteca Marzo (', cast(Count(*) as int), ') dia(s)') as Concepto, 0 as IVA
/*, Concat(c.PrimerNombre, ' ', c.ApellidoPaterno, ' ', c.ApellidoMaterno) AlumnoNombre*/
from lu_captura a
inner join ce_alumnoporciclo b on a.alumnoporcicloid = b.alumnoporcicloid
inner join ce_alumno c on b.alumnoid = c.alumnoid
where date(a.Fecha) between DATE_FORMAT(fecha, '%Y-%m-01') and LAST_DAY(fecha) and a.TieneContrato = 0 and c.alumnoid = alumnoid
group by a.TipoId, b.AlumnoId, b.GradoId
SET #LID = LAST_INSERT_ID();
update lu_captura set DocumentoPorPagarId = #LID where Fecha between DATE_FORMAT(fecha, '%Y-%m-01') and LAST_DAY(fecha)
Currently I have this query and the question is: How could I relate the document payable created to the corresponding type of library?
According to MySQL Stored Procedure
I am using this syntax in stored procedure if you work in the same
then you can try this
DROP Procedure IF EXISTS pagodeludotecaalumnos;
Delimitter to used to set whole procedure below it as single operationable
DELIMITER $$
Create procedure command
CREATE PROCEDURE pagodeludotecaalumnos (
IN alumnoid int, IN fecha varchar(50), IN_action char(30)
)
Case begin from here
BEGIN
**-- switch case begin here**
CASE
WHEN IN_action = "insert" THEN
Creating entry for new record
INSERT INTO cj_documentoporpagar(
documentoid, subconceptoid,pagoestatusid, alumnoid, cicloid,
gradoid,mediopagoid, importe,saldo, fechalimitepago, fechacreacion,
fechaprontopago, referencia,documento,hermanos, reingreso,
padreexalumno, concepto, iva
)
VALUES(
IN_documentoid, IN_subconceptoid, IN_pagoestatusid,
IN_alumnoid,IN_cicloid,IN_gradoid, IN_mediopagoid, IN_importe,
IN_saldo,IN_fechalimitepago, IN_fechacreacion,IN_fechaprontopago,
IN_referencia, IN_documento, IN_hermanos, IN_reingreso,
IN_padreexalumno, IN_concepto, IN_iva
);
-- Insert end here
For update
WHEN IN_action = "update" THEN
-- For update elements
-- I am only show you how i make stored procedure in mysql i.e. your update statement
UPDATE lu_captura SET DocumentoPorPagarId = #LID where Fecha between
DATE_FORMAT(fecha, '%Y-%m-01') and LAST_DAY(fecha)
WHERE condition(if_exists);
For select statement
-- For Select elements
-- i.e. your select statement because i don't know what are you want to update, store an insert
WHEN IN_action = "select" THEN
select 17 as DocumentoId,
case when a.TipoId = 1 or a.tipoid = 2 then 122
when a.TipoId = 3 then 235
when a.TipoId = 4 then -1 end as
SubConceptoId,
1 as PagoEstatusId, b.AlumnoId,
5 as CicloId, b.GradoId, 1 as MedioPagoId,
case when c.PrimerNombre like '%*%' or c.ApellidoPaterno
like '%*%' or c.ApellidoMaterno
like '%*%' then 40 * Count(*)
else 55 * Count(*) end as Importe,
case
when c.PrimerNombre like '%*%' or c.ApellidoPaterno
like '%*%' or c.ApellidoMaterno
like '%*%' then 40 * Count(*) else 55 * Count(*)
end as Saldo,
DATE(DATE_ADD(LAST_DAY(fecha), INTERVAL 1 MONTH)) as
FechaLImitePago, now() as FechaCreacion, now() as
FechaProntoPago, '' as Referencia, '202003L' as documento, 0
as Hermanos, 0 as Reingreso, 0 as PadreExAlumno,
Concat('Ludoteca Marzo (', cast(Count(*) as int), ') dia(s)')
as Concepto, 0 as IVA
/*, Concat(c.PrimerNombre, ' ', c.ApellidoPaterno, ' ',
c.ApellidoMaterno) AlumnoNombre*/
from lu_captura a
inner join ce_alumnoporciclo b on a.alumnoporcicloid =
b.alumnoporcicloid
inner join ce_alumno c on b.alumnoid = c.alumnoid
where
date(a.Fecha) between DATE_FORMAT(fecha, '%Y-%m-01') and
LAST_DAY(fecha) and a.TieneContrato = 0 and c.alumnoid =
alumnoid group by a.TipoId, b.AlumnoId, b.GradoId
END CASE;
-- switch case ends here
END;
-- end of the PROCEDURE
hello stackoverflow community :) ,
I have a complex join query is causing me lots of troubles :/
i have 3 tables here.
1: table [taxonomys t]
id ownerId type
1 1 office
2 1 inventory
3 1 inventory_item
2: table [tax_links l]
id parent son
1 1 2
2 1 3
3: table [settings s]
id taxId title value type
1 1 name office1 taxonomy
2 1 location Address taxonomy
3 1 settings on tax_links
so
1. taxonomy table containes all resourses of a user
2. link_taxs link 2 taxonomys to each others
3. settings save settings for resource , in case i want settings to be related to relations (not global) i set type in settings to tax_links
My query should return all resources of user, and concentrate all related as sons, and the id of relations in relsId.
SELECT `t`.`id`, group_concat(l.son) as sons, group_concat(l.id) as relsId, group_concat( s.title ) as titles, group_concat( s.value ) as vals, `t`.`name`, `t`.`type`, group_concat(s.id) as sid
FROM (`taxonomys` t)
LEFT JOIN `tax_links` l ON `l`.`parent` = `t`.`id`
LEFT JOIN `settings` s ON `s`.`taxId` = `t`.`id` and s.table = 'taxonomy'
WHERE `t`.`ownerId` = 1
GROUP BY `t`.`id`
it runs perfect, and return all what i need EXCEPT THAT it return REPLICATED results in sons,relsId.
for example tables i provided when i run this query i expect the result to be
id sons relsId titles vals
1 2,3 1,2 name,location office1,address
problem is when i run my query it return duplicate content for sons and relsId so i get something like
id sons relsId titles vals
1 2,3,2,3 1,2 name,location office1,address
name,location office1,address
why is this happeing ? i know i can filter array_unique using php after i fetch row, but what am i doing wrong ?
You want to use the distinct keyword in group_concat():
SELECT `t`.`id`, group_concat(distinct l.son) as sons,
group_concat(distinct l.id) as relsId,
group_concat( s.title ) as titles, group_concat( s.value ) as vals,
`t`.`name`, `t`.`type`, group_concat(s.id) as sid
I was just wondering whether there is a simple statement to get all columns of a row that equals a defined value.
I have a table which represent the rights a user has.
e.g.
userid | right A | right B | right C | right D
----------------------------------------------
1 | 0 | 0 | 1 | 1
----------------------------------------------
2 | 1 | 0 | 1 | 1
----------------------------------------------
3 | 0 | 0 | 0 | 1
i need a query that returns me all colums of a user that has the value 1
for user 2 the result would look like
'right A', 'right C', 'right D'
at the moment i load the complete row and iterate over the result to get the column names but i would love to do this directly in my query.
According to MySQL a where clause in show columns etc. only tests the name, but not a value of a specific row.
A query to filter all values out of a result that are not 1 would also help, but i must work without knowing all the column names :-/
maybe a procedure is an option but thats just outsourceing the code from php to mysql :-/
Any ideas or complete impossible?
Thank in advance! :)
Since, you mentioned that you are using MySQL, you can use IF on this
SELECT CONCAT_WS(', ', IF(`right A` = 1, 'right A', NULL),
IF(`right B` = 1, 'right B', NULL),
IF(`right C` = 1, 'right C', NULL),
IF(`right D` = 1, 'right D', NULL)) columnList
FROM tableName
WHERE userID = 2
SQLFiddle Demo
Create a dynamic sql for 50 columns, example
SET #dbaseName = (SELECT DATABASE()); // FILL DATABASE NAME HERE
SET #tableName = 'TableName'; // TableName
SET #userValue = 2; // value of UserID
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('IF(`',COLUMN_NAME , '` = 1, ''',COLUMN_NAME,''', NULL)')
) INTO #sql
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = #dbaseName AND
COLUMN_NAME LIKE 'right%';
SET #sql = CONCAT('SELECT CONCAT_WS('', '',', #sql, ') AS ColumnList
FROM tableName
WHERE userid = ', #userValue);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
I have a table of survey data where lines of results are separated into many rows by, each having their own variable name.
My table looks like this:
data_id data_content var_name var_line
1 1 SERIAL 1
2 2 GND.AGE 1
3 3 GND.NEWS.FREQ 1
4 2 SERIAL 2
5 3 GND.AGE 2
6 3 GND.NEWS.FREQ 2
7 3 SERIAL 3
8 3 GND.AGE 3
9 4 GND.NEWS.FREQ 3
Here is my current query to retrieve the total number of answers for every possible answer of GND.NEWS.FREQ. As in the total number:
SELECT *, COUNT(*) as total
FROM `data`
WHERE `var_name` = 'GND.NEWS.FREQ'
GROUP BY `data_content`
Now I need to add the functionality to only return answers where GND.AGE for example is 3. So basically treat all rows where var_line = 1 as a single row.
I have looked up pivot tables but I'm not sure how to add that into my current query.
I would like to do this in one query if possible but I wouldn't mind doing something like getting var_line IDs in a separate query.
Unfortunately MySQL does not have a PIVOT function which is basically what you are trying to do. So you will need to use an aggregate function with a CASE statement. If you have a unknown number of var_name values that you want to turn into columns, then you can use prepared statements:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when var_name = ''',
var_name,
''' then var_line end) AS ',
replace(var_name, '.', '_')
)
) INTO #sql
FROM data;
SET #sql = CONCAT('SELECT data_content,', #sql, '
from data
group by data_content
');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo