Find an average age range over a span of ages - mysql

I need to find the most occurrences in a 10yr age range that can be Age 2 to 22, 15 to 25, 10 to 20, etc. in a table with name & age
I've created the SQL that returns the average age:
SELECT age, count(age)
FROM member
GROUP BY age
ORDER BY COUNT(age) DESC
LIMIT 1
Thanks for your help!

Create another table ages to hold the age ranges you are interested in with a field for age_lower, age_upper and a display name age_range such as '2 to 22'
Join the tables with a WHERE clause that puts the age between the lower and upper ranges.
SELECT `age_range`, COUNT(`age`) AS age_count
FROM `member` INNER JOIN `ages`
ON age BETWEEN age_lower AND age_upper
GROUP BY age_range
ORDER BY COUNT(`age`) DESC, `age_range` ASC
SQL Fiddle

This might solve the problem. The only thing I added was a table to hold values 1..x where x is your bucket count. The #T can easily be replaced with your MySQL table name. The results are all possible sets the age falls in, for each age. Then count of how many equal sets.
--IGNORE BUILDING TEST DATA IN SQL SERVER
DECLARE #T TABLE(member INT,age INT)
DECLARE #X INT
SET #X=1
WHILE(#X<=100) BEGIN
INSERT INTO #T SELECT #X, CAST(RAND() * 100 AS INT)
SET #X=#X+1
END
DECLARE #MinAge INT=1
DECLARE #MaxAge INT=100
--YOUR SET TABLE. TO MAKE LIFE EASY YOU NEED A TABLE OF 1..X
DECLARE #SET TABLE (Value INT)
DECLARE #SET_COUNT INT =10
DECLARE #LOOP INT=1
WHILE(#LOOP<=#SET_COUNT) BEGIN
INSERT #SET SELECT #LOOP
SET #LOOP=#LOOP+1
END
SELECT
MinAge,
MaxAge,
SetCount=COUNT(CountFlag)
FROM
(
SELECT
MinAge=AgeMinusSetCount,
MaxAge=AgePlusSetCount,
CountFlag=1
FROM
(
SELECT DISTINCT
ThisAge,
AgeMinusSetCount=(AgeMinusSetCount-1) + Value,
AgePlusSetCount=CASE WHEN (AgeMinusSetCount-1) + Value + #SET_COUNT > #MaxAge THEN #MaxAge ELSE (AgeMinusSetCount-1) + Value + #SET_COUNT END
FROM
(
SELECT
ThisAge=age,
AgeMinusSetCount=CASE WHEN (age - #SET_COUNT) < #MinAge THEN #MinAge ELSE (age) - #SET_COUNT END
FROM
#T
)RANGES
LEFT OUTER JOIN (SELECT Value FROM #SET) AS FanLeft ON 1=1
)AS DETAIL
)AS Summary
GROUP BY
MinAge,
MaxAge
ORDER BY
COUNT(CountFlag) DESC

Related

Pre populate Sql data based on Enums or Constants

I am trying to fill db pre populated data from sql script where I have two type of constants or enums.
Platform: DG, NK
Department : KK, TG, LO, NP, UI, BG, ED, CC.
Task: To generate a sequential number using procedural loop and for each combination using above value we need to generate key and put in data base with count or sequence value.
Database columns are based on JPA abstract entity:
id, created_by, created_at, status, updated_by, updated_at, uuid, count, category_key
Now single row would be one combination which is formed using this pattern,
Department_Platform_SequenceNumber :: example => KK_DG_1,....KK_DG_10000, KK_NK_1,....KK_NK_10000
This is for 10k entries for 10k sequence of each combinations. It follows for other as well.
Approach:
DROP PROCEDURE KeyGeneration;
DELIMITER $$
CREATE PROCEDURE LoopDemo()
BEGIN
DECLARE x INT;
DECLARE dep VARCHAR(10);
DECLARE plat VARCHAR(10);
DECLARE str VARCHAR(30);
SET x = 1;
SET dep = 'KK'; # help to initialize enums or constants
SET plat = 'DG'; # help to initialize enums or constants
count_val: LOOP
IF x=10000 THEN
LEAVE count_val;
END IF;
SET str = CONCAT(dep,'_',plat,'_',x);
insert into counter_key values(id, created_by, created_at, status, updated_by, updated_at, uuid, x, str);
SET x = x + 1;
END LOOP;
END$$
DELIMITER ;
call LoopDemo();
but this is wrong since what I want is that atleast id to be updated if possible created_at and other fields as well also the loop will return last value I guess I want to get each value.
TABLE COLUMNS UPDATED
UPDATED: id, count, counter_key, status
#Akina answer applied as per my new table but syntax error
WITH RECURSIVE
number AS ( SELECT 1 number
UNION ALL
SELECT number + 1 FROM cte WHERE number < 10000 ),
platform AS ( SELECT 'DG' platform
UNION ALL
SELECT 'NK' ),
department AS ( SELECT 'KK' department
UNION ALL
SELECT 'TG'
UNION ALL
SELECT 'LO'
UNION ALL
SELECT 'NP'
UNION ALL
SELECT 'UI'
UNION ALL
SELECT 'BG'
UNION ALL
SELECT 'ED'
UNION ALL
SELECT 'CC' )
INSERT INTO counter_key
SELECT null, number, CONCAT_WS('_', department, platform, number), 1
FROM department
CROSS JOIN platform
CROSS JOIN number;
WITH RECURSIVE
number AS ( SELECT 1 number
UNION ALL
SELECT number + 1 FROM cte WHERE number < 10000 ),
platform AS ( SELECT 'DG' platform
UNION ALL
SELECT 'NK' ),
department AS ( SELECT 'KK' department
UNION ALL
SELECT 'TG'
UNION ALL
SELECT 'LO'
UNION ALL
SELECT 'NP'
UNION ALL
SELECT 'UI'
UNION ALL
SELECT 'BG'
UNION ALL
SELECT 'ED'
UNION ALL
SELECT 'CC' )
INSERT INTO counter_key
SELECT #id, #created_by, #created_at, #status, #updated_by, #updated_at, #uuid, #x,
CONCAT_WS('_', department, platform, number)
FROM department
CROSS JOIN platform
CROSS JOIN number;
where #id, #created_by, #created_at, #status, #updated_by, #updated_at, #uuid, #x must be replaced with literal values taken from somewhere.

SQL IDENTITY column based on another column

Is there a way to define an identity column on another column? What I want to accomplish is a table that holds positions of an order and these orders can be put there anytime. So it could be that there are already lets say three positions in the table and it would look somewhat like this:
OrderNumber | OrderPosition
10001 1
10001 2
10001 3
And now I want to add another position without calculating the right value for the OrderPosition column. This is because I want to write new positions for multiple orders into the table and would like to avoid cursoring over the individual orders. I would prefer a solution wher OrderPosition is an identity column that is reseeded based on the OrderNumber column. So that If i add an order position for a new order it would start with 1 and if I add another position for order 10001 it would continue with 4.
Write a Scalar Function that returns the MAX(OrderPosition) based on OrderNumber. Then reference that function in the insert statement of orders
your requirement will not work for identity column.
You need to create custom logic to get from the normal columns and on combination based new no will generate.. like (read comments, only choose one logic)
declare #t table(OrderNumber int, OrderPosition int)
insert into #t values (10001, 1),(10001, 2),(10001, 3),(10001, 4)
select * from #t
--now insert new record with old orderno
declare #seq int = 1
declare #ordernumberNew int = 10001
--Eigher you can use :- insert to more understand
if( exists(select orderposition from #t where OrderNumber = #ordernumberNew ))
begin
set #seq = (select max(OrderPosition) + 1 from #t where OrderNumber = #ordernumberNew )
end
insert into #t values (#ordernumberNew , #seq )
select * from #t
--or another twist of above statement, insert directly as
insert into #t
values
(
#ordernumberNew,
case when exists (select orderposition from #t where OrderNumber = #ordernumberNew )
then (select max(OrderPosition) + 1 from #t where OrderNumber = #ordernumberNew )
else 1 end
)
select * from #t
--Now enter the not exist order no
set #ordernumberNew = 10006
insert into #t
values
(
#ordernumberNew,
case when exists (select orderposition from #t where OrderNumber = #ordernumberNew )
then (select max(OrderPosition) + 1 from #t where OrderNumber = #ordernumberNew )
else 1 end
)
select * from #t

MySQL - Insert multiple rows based on column value

I have the query working, just wondering if there is a better way to do this without cursors/loops/php side. I've been a DBA for 5+ years and just came across the := statement. Very cool.
Table (tblPeople) with the person ID and the number of tickets they bought.
PersonId NumTickets
1 3
2 1
3 1
I then want to assign individual tickets to each person in a new table (tblTickets), depending on how many tickets they bought. The TicketId is a key, auto increment column.
TicketId PersonId
100 1
101 1
102 1
103 2
104 3
Here is the code. It loops through the whole tblPeople over and over again incrementing a new calculated column called rowID. Then I filter out the rows based on the number of tickets they bought in the WHERE clause. The problem I see is the subquery is huge, the more people I have, the bigger the subquery gets. Just not sure if there is a better way to write this.
INSERT INTO tblTickets (PersonId)
SELECT PersonId
FROM (
SELECT s.PersonId, s.NumTickets,
#rowID := IF(#lastPersonId = s.PersonId and #lastNumTickets = s.NumTickets, #rowID + 1, 0) AS rowID,
#lastPersonId := s.PersonId,
#lastNumTickets := s.NumTickets
FROM tblPeople m,
(SELECT #rowID := 0, #lastPersonId := 0, #lastNumTickets := 0) t
INNER JOIN tblPeople s
) tbl
WHERE rowID < NumTickets
I'd add a utility table Numbers which contains all the numbers from 1 up to the maximal number of tickets a person may buy. Then you can do something like this:
INSERT INTO tblTickets (PersonId)
SELECT s.PersonId
FROM tblPeople s, Numbers n
WHERE n.number <= s.NumTickets
Following Stored procedure will serve your purpose...
DELIMITER $$
USE <your database name> $$
DROP PROCEDURE IF EXISTS `update_ticket_value2`$$
CREATE PROCEDURE `update_ticket_value2`()
BEGIN
DECLARE index_value INT;
DECLARE loop_variable INT;
SET #KeyValue = 100;
SET #LastPersonID = 0;
SET #TicketNum = 0;
SET #PersonIDToHandle = 0;
SELECT #PersonIDToHandle = PersonID, #TicketNum = NumTickets
FROM tblPeople
WHERE PersonId > #LastPersonID
ORDER BY PersonId
LIMIT 0,1;
WHILE #PersonIDToHandle IS NOT NULL
DO
SET loop_variable = 0;
WHILE(loop_variable < #TicketNum) DO
INSERT INTO tblTickets(TicketId, PersonId) VALUES(#KeyValue + loop_variable, #PersonIDToHandle);
SET loop_variable = loop_variable + 1;
END WHILE;
SET #LastPersonID = #PersonIDToHandle;
SET #PersonIDToHandle = NULL;
SET #KeyValue = #KeyValue + #TicketNum;
SELECT #PersonIDToHandle := PersonID, #TicketNum := NumTickets
FROM tblPeople
WHERE PersonId > #LastPersonID
ORDER BY PersonId
LIMIT 0,1;
END WHILE;
END$$
DELIMITER ;
Call the procedure as:
CALL update_ticket_value2();
Hope it helps...

Sql query to fetch the next token number

I have a column TokenId list in my SQL table "Tokendata".
The TokenId has values say 1,2,3,4,6,7,8,10,11
Here the above tokenIds are occupied. When I query this table, it should give me the next free tokenId, in this case it should be 5.
If TokenId has values say "1,2,3,4,5,6,7,8,10,11,13,20", the query should return me 9.
Can anyone help me with a SQL query for this?
Note: updated to account for cases where 1 is missing, as per the comment.
SELECT
t.TokenId + 1
FROM (
SELECT TokenId
FROM Tokendata
UNION ALL
SELECT 0
) t
LEFT JOIN Tokendata t2 ON t.TokenId = t2.TokenId - 1
WHERE t2.TokenId IS NULL
ORDER BY t.TokenId
LIMIT 1
Here is the answer to my question. But it may not be the optimistic solution. Optimized solutions are welcome
DECLARE #TempTable TABLE
(
TableID int PRIMARY KEY IDENTITY,
TokenId int
)
Insert Into #TempTable
Select tt.TokenId from XLBDataPoint tt where tt.TokenId= 1
DECLARE #RowCount INT
SET #RowCount = (SELECT COUNT(*) FROM #TempTable )
(SELECT
Top 1 t.TokenId + #RowCount
FROM TokenData t
Where t.TokenId+ #RowCount NOT in
(select t2.TokenId FROM TokenData t2)

Calculating a total in SQL Server

I'm having trouble trying to calculate a running total from within a CASE statement.
I have two tables #report and #question and two variables #countCurrent and #countSuggested.
Based off the numbers in the #report table compared against either a static value or a value from the #question table I need to increment either #countCurrent or #countSuggested.
Here is what I have so far but instead of getting some combination of 5 in either column, I am only getting 0/1. I think it is part of the JOIN but I can't see what.
declare #MinSuccessRate float,
#countCurrent int,
#countSuggested int
declare #report table
(
intID int identity(1,1),
intReportID int,
intParticipantID int,
acceptable float,
optimum float
)
insert #report
select 1,1,.25,.75 union all
select 1,2,.45,.75 union all
select 1,3,.35,.75 union all
select 1,4,.55,.75 union all
select 1,5,.65,.75
declare #question table
(
intID int identity(1,1),
intParticipantID int,
answer float
)
insert #question
select 1,35 union all
select 1,55 union all
select 1,65 union all
select 1,75 union all
select 1,85
SET #MinSuccessRate=0.75
SET #countCurrent=0
SET #countSuggested=0
UPDATE #report
SET #countCurrent=
CASE WHEN acceptable>=#MinSuccessRate
THEN #countCurrent+1
ELSE 0
END,
#countSuggested=
CASE WHEN optimum*100 >=q.answer
THEN #countSuggested+1
ELSE 0
END
FROM #report pr
INNER JOIN #question q
ON pr.intParticipantID=q.intParticipantID
WHERE pr.intReportID=1
select #countCurrent [Current],#countSuggested [Suggested]
Thanks in advance!
In a multiple table UPDATE, each target record can be updated at most once (regardless of how many times is it returned by the join).
However, you don't need UPDATE here at all:
SELECT #countCurrent =
SUM
(
CASE
WHEN acceptable >= #MinSuccessRate
THEN
1
ELSE
0
END
),
#countSuggested =
SUM
(
CASE
WHEN optimum * 100 >= q.answer
THEN
1
ELSE
0
END
)
FROM #report pr
JOIN #question q
ON q.intParticipantID = pr.intParticipantID
WHERE pr.intReportID = 1
You may check this http://www.1keydata.com/sql/sql-running-totals.html