Select and show according to the year/monts - mysql

can you help me to select, and sum all the values from the table, and show them according to the months/this year?
Table looks as follow"
| ID | value | reg_date |
| 1 | 2 | 2017-01-11 02:26:22 |
| 2 | 2 | 2017-03-12 12:22:23 |
| 3 | 2 | 2017-04-13 08:26:33 |
| 4 | 3 | 2017-04-15 12:26:16 |
| 5 | 5 | 2017-05-15 19:26:13 |
| 6 | 2 | 2017-06-14 17:12:16 |
| 7 | 6 | 2017-07-12 14:26:16 |
| 8 | 1 | 2015-09-11 13:23:16 |
| 9 | 1 | 2016-09-05 12:26:34 |
| 10 | 1 | 2017-12-11 19:11:45 |
And I would like to get something like:
| value | reg_date |
| 2 | 2017-01 |
| 0 | 2017-02 |
| 2 | 2017-03 |
| 5 | 2017-04 |
| 5 | 2017-05 |
| 2 | 2017-06 |
| 6 | 2017-07 |
| 0 | 2017-08 |
| 2 | 2017-09 |
| 0 | 2017-10 |
| 0 | 2017-11 |
| 1 | 2017-12 |

I tried following approach to solve your problem:
--created sample table datte which will contain following records
create table datte (id int identity(1,1), value int, reg_date datetime);
insert into datte(value,reg_date) values (2,'2017-01-11 02:26:22');
insert into datte(value,reg_date) values (2,'2017-03-12 12:22:23');
insert into datte(value,reg_date) values (2,'2017-04-13 08:26:33');
insert into datte(value,reg_date) values (3,'2017-04-15 12:26:16');
insert into datte(value,reg_date) values (5,'2017-05-15 19:26:13');
insert into datte(value,reg_date) values (2,'2017-06-14 17:12:16');
insert into datte(value,reg_date) values (6,'2017-07-12 14:26:16');
insert into datte(value,reg_date) values (1,'2015-09-11 13:23:16');
insert into datte(value,reg_date) values (1,'2016-09-05 12:26:34');
insert into datte(value,reg_date) values (1,'2017-12-11 19:11:45');
then i created a table type to store the year list
create type [values] as table(
[year] int
);
You can also use temp table here.
-- create temp table to store the output
create table #tempdate([year] varchar(1000), [count] int);
--create a variable of table type values
declare #years as [values] ,
--create following variable to process the data
#year int, #minyear int,#maxyear int;
--insert years in table type variable #years
insert into #years
select YEAR(reg_date) from datte
group by YEAR(reg_date)
--store min year and max year value in variable for processing
select #minyear = min([year]) from #years;
select #maxyear = max([year]) from #years;
-- logic to create query to get desired output
while(#minyear <= #maxyear)
begin
SET #YEAR = #minyear;
IF EXISTS(SELECT 1 FROM DATTE WHERE REG_DATE LIKE '%'+CONVERT(VARCHAR,#YEAR)+'%')
BEGIN
--insert the yearly in temp table
insert into #tempdate
select cast(convert(varchar,#year) as varchar) + '-' +cast(m.number as varchar) , isnull(total_qty,0) as total_qty
from (
select number
from master.dbo.spt_values
where type = 'P' and number between 01 and 12
) m
left join (
select MONTH(reg_date) as mth, SUM(value) as total_qty
from datte
where YEAR(reg_date) = #year
group by MONTH(reg_date)
) s on m.number = s.mth ;
END
SET #MINYEAR = #MINYEAR + 1;
end
--print the data
select [count],[year] from #tempdate
hope this helps,
Jai

You can format how date is displayed and group by this results
SELECT count(ID), DATE_FORMAT(reg_date, "%Y-%m") AS rd FROM tablename GROUP BY rd

Also works something like:
SELECT start_date,
IFNULL(TIME_FORMAT(SEC_TO_TIME (sum(value)),'%l.%i' ),0)
as time01 FROM table WHERE YEAR(start_date) = YEAR(CURDATE())
and MONTH(start_date) = 01
SELECT start_date,
IFNULL(TIME_FORMAT(SEC_TO_TIME (sum(value)),'%l.%i' ),0)
as time02 FROM table WHERE YEAR(start_date) = YEAR(CURDATE())
and MONTH(start_date) = 02
....
Thanks guys a lot.

Related

Join a table and calculate a percentage from this new table

I'm trying to make a report of financial datas for my company:
I have actually two two tables:
___BillableDatas:
|--------|------------|----------|----------|--------------|---------------------|
| BIL_Id | BIL_Date | BIL_Type | BIL_Rate | BIL_Quantity | BIL_ApplicableTaxes |
|--------|------------|----------|----------|--------------|---------------------|
| 1 | 2017-01-01 | Night | 95 | 1 | 1 |
| 2 | 2017-01-02 | Night | 95 | 1 | 1 |
| 3 | 2017-01-15 | Night | 105 | 1 | 1 |
| 4 | 2017-01-15 | Item | 8 | 2 | 1,2 |
| 5 | 2017-02-14 | Night | 95 | 1 | 1 |
| 6 | 2017-02-15 | Night | 95 | 1 | 1 |
| 7 | 2017-02-16 | Night | 95 | 1 | 1 |
| 8 | 2017-03-20 | Night | 89 | 1 | 1 |
| 9 | 2017-03-21 | Night | 89 | 1 | 1 |
| 10 | 2017-03-21 | Item | 8 | 3 | 1,2 |
|--------|------------|----------|----------|--------------|---------------------|
___SalesTaxes:
|--------|------------|
| STX_Id | STX_Amount |
|--------|------------|
| 1 | 14.00 |
| 2 | 5.00 |
|--------|------------|
I need to know for each month the sum of my revenue with and without taxes.
Actually I can make the report but don't know how to loop into the ___SalesTaxes table.
What I have actually:
SELECT month(BIL_Date) AS month,
sum(BIL_Rate * BIL_Quantity) AS sumval
FROM `___BillableDatas`
WHERE BIL_Date BETWEEN "2017-01-01" AND "2017-12-31"
AND BIL_Type = "Night" OR BIL_Type = "Item"
GROUP BY year(BIL_Date), month(BIL_Date)
Thanks for your help.
as kbball mentioned you have an unresolved many to many relationship in your main table. A proper table should never be designed to have more than one value per field. Resolving many to many relationships is quite simple. You will need to create a new table something like bill_taxType or some relation like that. The new table would have two fields as well as the standard primary key, it will have bill_id and applicable tax id. In the case of your 1,2 fields like bill id 4 in the new table it will look like
primary key, bill id, applicable tax id
1 4 1
2 4 2
In your final query you will join all three together on the appropriate primary key-foreign key relationship. This final query should have the data that you need.
This will work, I've created following example will help you lot for debugging and implementation. try to implement as below :
If(OBJECT_ID('tempdb..#___BillableDatas') Is Not Null)
Begin
Drop Table #___BillableDatas
End
If(OBJECT_ID('tempdb..#___SalesTaxes') Is Not Null)
Begin
Drop Table #___SalesTaxes
End
CREATE TABLE #___BillableDatas
(
BIL_Id INT IDENTITY (1,1),
BIL_Date DATETIME,
BIL_Type VARCHAR(50),
BIL_Rate FLOAT,
BIL_Quantity INT,
BIL_ApplicableTaxes VARCHAR(10)
);
INSERT INTO #___BillableDatas (BIL_Date,BIL_Type,BIL_Rate,BIL_Quantity,BIL_ApplicableTaxes)
VALUES ('2017-01-01','Night',95,1,'1'),
('2017-01-02','Night',95,1,'1'),
('2017-01-15','Night',105,1,'1'),
('2017-01-15','Item',8,2,'1,2'),
('2017-02-14','Night',95,1,'1'),
('2017-02-15','Night',95,1,'1'),
('2017-02-16','Night',95,1,'1'),
('2017-03-20','Night',89,1,'1'),
('2017-03-21','Night',89,1,'1'),
('2017-03-21','Item',8,1,'1,2')
CREATE TABLE #___SalesTaxes
(
STX_Id INT IDENTITY (1,1),
STX_Amount FLOAT
);
INSERT INTO #___SalesTaxes (STX_Amount) VALUES (14.00),(5.00)
-----------------------------------------------------------------
SELECT * FROM #___BillableDatas
SELECT * FROM #___SalesTaxes
SELECT MONTH(BD.BIL_Date) AS [Month],SUM(BD.BIL_Rate * BD.BIL_Quantity) AS 'Without Tax'
,(SUM(BD.BIL_Rate * BD.BIL_Quantity)+((SUM(BD.BIL_Rate * BD.BIL_Quantity)/100)*BD.Tax1)) AS 'With Tax 1'
,(SUM(BD.BIL_Rate * BD.BIL_Quantity)+((SUM(BD.BIL_Rate * BD.BIL_Quantity)/100)*BD.Tax2)) AS 'With Tax 2'
FROM
(
SELECT *,
(SELECT ST1.STX_Amount FROM Func_Split(BIL_ApplicableTaxes,',') AS F LEFT JOIN #___SalesTaxes AS ST1 ON F.Element=ST1.STX_Id WHERE F.Element='1') AS Tax1 ,
(SELECT ST1.STX_Amount FROM Func_Split(BIL_ApplicableTaxes,',') AS F LEFT JOIN #___SalesTaxes AS ST1 ON F.Element=ST1.STX_Id WHERE F.Element='2') AS Tax2
FROM #___BillableDatas) AS BD
WHERE BD.BIL_Date BETWEEN '2017-01-01' AND '2017-12-31' AND BD.BIL_Type = 'Night' OR BD.BIL_Type = 'Item'
GROUP BY YEAR(BD.BIL_Date), MONTH(BD.BIL_Date),BD.Tax1,BD.Tax2
You will require function Func_Split for above solution, use this :
CREATE FUNCTION [dbo].[func_Split]
(
#DelimitedString varchar(8000),
#Delimiter varchar(100)
)
RETURNS #tblArray TABLE
(
ElementID int IDENTITY(1,1), -- Array index
Element varchar(1000) -- Array element contents
)
AS
BEGIN
-- Local Variable Declarations
-- ---------------------------
DECLARE #Index smallint,
#Start smallint,
#DelSize smallint
SET #DelSize = LEN(#Delimiter)
-- Loop through source string and add elements to destination table array
-- ----------------------------------------------------------------------
WHILE LEN(#DelimitedString) > 0
BEGIN
SET #Index = CHARINDEX(#Delimiter, #DelimitedString)
IF #Index = 0
BEGIN
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(#DelimitedString)))
BREAK
END
ELSE
BEGIN
INSERT INTO
#tblArray
(Element)
VALUES
(LTRIM(RTRIM(SUBSTRING(#DelimitedString, 1,#Index - 1))))
SET #Start = #Index + #DelSize
SET #DelimitedString = SUBSTRING(#DelimitedString, #Start , LEN(#DelimitedString) - #Start + 1)
END
END
RETURN
END

Mysql - Check if VARCHAR column has a missing value on its incrementation

I'm trying to find out if my values inserted are auto-incrementing correctly or if for any reason one has failed to be inserted, deleted or gone "missing". I've tried several answers from Stackoverflow but they were mainly pointing out autoincrementable int values so they did not help since mine is a VARCHAR value that follows the following sequence:
AA000001
AA000002
...
AA000100
...
AA213978
and so on...
Thanks for your time.
You can declare SQL Vars in Query and calculate the difference in each iteration, as shown in the example below:
Schema
create table MyTable
( ai int auto_increment primary key,
id varchar(100) not null
);
insert MyTable (id) values
('AA000001'),
('AA000002'),
('AA000005'),
('AA000008'),
('AA000009'),
('AA000010');
Query
select id
FROM
(
select
t.id,
SUBSTRING(t.id,3) as s,
CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId as diff,
if( #lastId = 0, 0, CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId) as Difference,
#lastId := CAST(SUBSTRING(t.id,3) AS UNSIGNED) as dummy
from
`MyTable` t,
( select #lastId := 0) SQLVars
order by
t.id
) d
WHERE diff>1;
This is the inside query (not the final result set of the above)
+----------+--------+------+------------+-------+
| id | s | diff | Difference | dummy |
+----------+--------+------+------------+-------+
| AA000001 | 000001 | 1 | 0 | 1 |
| AA000002 | 000002 | 1 | 1 | 2 |
| AA000005 | 000005 | 3 | 3 | 5 |
| AA000008 | 000008 | 3 | 3 | 8 |
| AA000009 | 000009 | 1 | 1 | 9 |
| AA000010 | 000010 | 1 | 1 | 10 |
+----------+--------+------+------------+-------+
Actual Results of Above Query:
+----------+
| id |
+----------+
| AA000005 |
| AA000008 |
+----------+
Here's the SQL Fiddle.
To simply test if there are missing values,
select count(*) <> max(right(col, 6))-min(right(col, 6))+1 || count(*) <> count(distinct col)

MySQL count changes

I would like to count number of changes in column Value grouped by Id using MySQL.
Source Table:
create table sequence
(
`Id` int,
`Date` date,
`Value` int not null,
PRIMARY KEY (`Id`,`Date`)
);
insert into sequence
( `Id`,`Date`, `Value` )
values
(1, '2016-01-01' , 0 ),
(1, '2016-01-02' , 10 ),
(1, '2016-01-03' , 0 ),
(1, '2016-01-05' , 0 ),
(1, '2016-01-06' , 10 ),
(1, '2016-01-07' , 15 ),
(2, '2016-01-08' , 15 );
Visualization:
+------------+-------+-------+
| Date | ID | Value |
+------------+-------+-------+
| 2016-01-01 | 1 | 0 |
| 2016-01-02 | 1 | 10 | (change)
| 2016-01-03 | 1 | 0 | (change)
| 2016-01-05 | 1 | 0 |
| 2016-01-06 | 1 | 10 | (change)
| 2016-01-07 | 1 | 15 | (change)
| 2016-01-08 | 2 | 15 |
+------------+-------+-------+
Expected output:
+-------+-------+
| ID | Value |
+-------+-------+
| 1 | 4 |
| 2 | 0 |
+-------+-------+
I would like to ask if there is a way how to do this in SQL.
This is not the very efficient or elegant solution,
but just to show some goals that you can achieve using mysql :-)
http://sqlfiddle.com/#!9/1db14/6
SELECT t1.id, MAX(t1.changes)
FROM (SELECT t.*,
IF (#i IS NULL,#i:=0,IF(#lastId <> id,#i:=0,IF (#lastV <> `value`, #i:=#i+1, #i:=#i))) as changes,
#lastV := `value`,
#lastId := `id`
FROM (
SELECT *
FROM sequence
ORDER BY id, date) t
) t1
GROUP BY t1.id

SQL increment date using recursion

I have a table which contains the following structure:
|-----------|------------|-----------|
| Number | Date | Subject |
|-----------|------------|-----------|
| 1 | 2015-01-01 | ABC |
| 2 | 2015-01-01 | ABC |
| 3 | 2015-01-01 | ABC |
| 4 | 2015-01-01 | ABC |
|-----------|------------|-----------|
I need to loop through the table and increment of n days each date.
So, assuming n = 10 I should get this result:
|-----------|------------|-----------|
| Number | Date | Subject |
|-----------|------------|-----------|
| 1 | 2015-01-01 | ABC |
| 2 | 2015-01-11 | ABC |
| 3 | 2015-01-21 | ABC |
| 4 | 2015-01-31 | ABC |
|-----------|------------|-----------|
The problem is a bit more complicated because n is generated by using a function which needs the previous generated date
I am trying to use CTE to accomplish this with the following CTE, but I get too many rows than expected.
WITH myCte(Number, Date, Subject)
AS
(
SELECT * FROM MyTable
UNION ALL
SELECT
Number, dbo.get_next_date(Date)
FROM MyCte
)
SELECT * FROM MyCte
That is because you have no WHERE clause in the recursive CTE. This would cause the query to stop when MAXRECURSION value is reached (default 100).
Here is an example to set a limit with a WHERE clause
DECLARE #MyTable TABLE
(
Number int,
Dt Date,
Sub CHAR(3)
)
INSERT INTO #MyTable
VALUES
(1,'2015-01-01','ABC'),
(2,'2015-01-11','ABC'),
(3,'2015-01-21','ABC'),
(4,'2015-01-31','ABC')
;WITH myCte(Number, Date, Subject)
AS
(
SELECT * FROM #MyTable
UNION ALL
SELECT
Number, DATEADD(day, 10, Date),Subject
FROM MyCte
WHERE Date < GETDATE()
)
SELECT * FROM MyCte
EDIT - If you know the number of rows, then you could just use TOP and get those rows.
SELECT TOP 4 * FROM MyCte

Find and Delete Duplicate rows in MySQL

I'm having trouble finding duplicates in a database table with the following setup:
==========================================================================
| stock_id | product_id | store_id | stock_qty | updated_at |
==========================================================================
| 9990 | 51 | 1 | 13 | 2014-10-25 16:30:01 |
| 9991 | 90 | 2 | 5 | 2014-10-25 16:30:01 |
| 9992 | 161 | 1 | 3 | 2014-10-25 16:30:01 |
| 9993 | 254 | 1 | 18 | 2014-10-25 16:30:01 |
| 9994 | 284 | 2 | 12 | 2014-10-25 16:30:01 |
| 9995 | 51 | 1 | 11 | 2014-10-25 17:30:02 |
| 9996 | 90 | 2 | 5 | 2014-10-25 17:30:02 |
| 9997 | 161 | 1 | 3 | 2014-10-25 17:30:02 |
| 9998 | 254 | 1 | 16 | 2014-10-25 17:30:02 |
| 9999 | 284 | 2 | 12 | 2014-10-25 17:30:02 |
==========================================================================
Stock updates are imported into this table every hour, I'm trying to find duplicate stock entries (any rows which have a matching product id and store id) so I can delete the oldest. The query below is my attempt, by comparing product ids and store ids on a join like this I can find one set of duplicates:
SELECT s.`stock_id`, s.`product_id`, s.`store_id`, s.`stock_qty`, s.`updated_at`
FROM `stock` s
INNER JOIN `stock` j ON s.`product_id`=j.`product_id` AND s.`store_id`=j.`store_id`
GROUP BY `stock_id`
HAVING COUNT(*) > 1
ORDER BY s.updated_at DESC, s.product_id ASC, s.store_id ASC, s.stock_id ASC;
While this query will work, it doesn't find ALL duplicates, only 1 set, which means if an import goes awry and isn't noticed until the morning, there's a possibility that we'll be left with tons of duplicate stock entries. My MySQL skills are sadly lacking and I'm at a complete loss about how to find and delete all duplicates in a fast, reliable manner.
Any help or ideas are welcome. Thanks
You can use this query:
DELETE st FROM stock st, stock st2
WHERE st.stock_id < st2.stock_id AND st.product_id = st2.product_id AND
st.store_id = st2.store_id;
This query will delete older record having same product_id and store_id and will keep latest record.
A self join on store_id, product_id and 'is older' in combination with DISTINCT should give you all rows where also a newer version exists:
> SHOW CREATE TABLE stock;
CREATE TABLE `stock` (
`stock_id` int(11) NOT NULL,
`product_id` int(11) DEFAULT NULL,
`store_id` int(11) DEFAULT NULL,
`stock_qty` int(11) DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`stock_id`)
> select * from stock;
+----------+------------+----------+-----------+---------------------+
| stock_id | product_id | store_id | stock_qty | updated_at |
+----------+------------+----------+-----------+---------------------+
| 1 | 1 | 1 | 1 | 2001-01-01 12:00:00 |
| 2 | 2 | 2 | 1 | 2001-01-01 12:00:00 |
| 3 | 2 | 2 | 1 | 2002-01-01 12:00:00 |
+----------+------------+----------+-----------+---------------------+
> SELECT DISTINCT s1.stock_id, s1.store_id, s1.product_id, s1.updated_at
FROM stock s1 JOIN stock s2
ON s1.store_id = s2.store_id
AND s1.product_id = s2.product_id
AND s1.updated_at < s2.updated_at;
+----------+----------+------------+---------------------+
| stock_id | store_id | product_id | updated_at |
+----------+----------+------------+---------------------+
| 2 | 2 | 2 | 2001-01-01 12:00:00 |
+----------+----------+------------+---------------------+
> DELETE stock FROM stock
JOIN stock s2 ON stock.store_id = s2.store_id
AND stock.product_id = s2.product_id
AND stock.updated_at < s2.updated_at;
Query OK, 1 row affected (0.02 sec)
> select * from stock;
+----------+------------+----------+-----------+---------------------+
| stock_id | product_id | store_id | stock_qty | updated_at |
+----------+------------+----------+-----------+---------------------+
| 1 | 1 | 1 | 1 | 2001-01-01 12:00:00 |
| 3 | 2 | 2 | 1 | 2002-01-01 12:00:00 |
+----------+------------+----------+-----------+---------------------+
Or you can use a stored Procedure:
DELIMITER //
DROP PROCEDURE IF EXISTS removeDuplicates;
CREATE PROCEDURE removeDuplicates(
stockID INT
)
BEGIN
DECLARE stockToKeep INT;
DECLARE storeID INT;
DECLARE productID INT;
-- gets the store and product value
SELECT DISTINCT store_id, product_id
FROM stock
WHERE stock_id = stockID
LIMIT 1
INTO
storeID, productID;
SELECT stock_id
FROM stock
WHERE product_id = productID AND store_id = storeID
ORDER BY updated_at DESC
LIMIT 1
INTO
stockToKeep;
DELETE FROM stock
WHERE product_id = productID AND store_id = storeID
AND stock_id != stockToKeep;
END //
DELIMITER ;
And afterwards call it for every pair of the product id and store id via a cursor procedure:
DELIMITER //
CREATE PROCEDURE updateTable() BEGIN
DECLARE done BOOLEAN DEFAULT FALSE;
DECLARE stockID INT UNSIGNED;
DECLARE cur CURSOR FOR SELECT DISTINCT stock_id FROM stock;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;
OPEN cur;
testLoop: LOOP
FETCH cur INTO stockID;
IF done THEN
LEAVE testLoop;
END IF;
CALL removeDuplicates(stockID);
END LOOP testLoop;
CLOSE cur;
END//
DELIMITER ;
And then just call the second procedure
CALL updateTable();