Table 1.
CREATE TABLE `admin_users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` int(11) DEFAULT '0',
`landingpage` int(11) DEFAULT '8',
`user_role_id` int(11) DEFAULT '0',
`user_parent_role` int(11) DEFAULT '0',
`bank_branch_id` int(11) DEFAULT '0',
`status` enum('1','0') COLLATE utf8_unicode_ci DEFAULT '1',
`firstname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`lastname` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`), KEY `user_role_id` (`user_role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=281 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='الموظفين';
Table 2.
CREATE TABLE `application_activity` (
`activityid` bigint(11) NOT NULL AUTO_INCREMENT,
`dataid` text,
`datatable` varchar(255) DEFAULT NULL,
`userid` int(11) DEFAULT '0',
`activitytype` char(1) DEFAULT 'I',
`activitytime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`activityip` varchar(15) DEFAULT NULL,
`activitydevice` text,
PRIMARY KEY (`activityid`)) ENGINE=MyISAM AUTO_INCREMENT=152862 DEFAULT CHARSET=utf8 COMMENT='نشاط التطبيق';
Query I am executing.
SELECT admin_users.`firstname`,admin_users.`lastname`,admin_users.`id`, branches.`branch_name`,
(SELECT COUNT(activityid) FROM application_activity WHERE userid=admin_users.`id`) AS cnt,
(SELECT activitytype FROM application_activity WHERE userid=admin_users.`id` ORDER BY activityid DESC LIMIT 0,1) AS activitytype,
(SELECT datatable FROM application_activity WHERE userid=admin_users.`id` ORDER BY activityid DESC LIMIT 0,1) AS datatable,
(SELECT activitytime FROM application_activity WHERE userid=admin_users.`id` ORDER BY activityid DESC LIMIT 0,1) AS activitytime
FROM admin_users
JOIN branches ON branches.`branch_id`=admin_users.`branch_id`
HAVING cnt > 0
ORDER BY cnt DESC
Table 1 have 100 records of users and Table 2 have more then 100k records of application activity.
when i execute query it will take 4min for giving result.
For this query:
This is your query:
SELECT au.`firstname`, au.`lastname`, au.`id`, b.`branch_name`,
(SELECT COUNT(*) FROM application_activity aa WHERE aa.userid = admin_users.`id`) AS cnt,
(SELECT aa.activitytype FROM application_activity aa WHERE aa.userid = au.`id` ORDER BY aa.activityid DESC LIMIT 0,1) AS activitytype,
(SELECT aa.datatable FROM application_activity aa WHERE aa.userid = au.`id` ORDER BY aa.activityid DESC LIMIT 0,1) AS datatable,
(SELECT aa.activitytime FROM application_activity aa WHERE aa.userid = au.id` ORDER BY aa.activityid DESC LIMIT 0,1) AS activitytime
FROM admin_users au JOIN
branches b
ON b.`branch_id` = au.`branch_id`
HAVING cnt > 0
ORDER BY cnt DESC;
You need appropriate indexes. One is on branches(branch_id) -- but you might have this already.
The second is application_activity(userid, activityid).
I strongly recommend that when all columns in correlated subqueries be qualified with the table alias.
Related
I've been trying to even write this title in a way that makes most sense, but through much googling I have not found anything to match what I am looking.
Basically, I have a database that stores players and their different levels, and I already have a working query that fetches a list of all players and ranks them in the required order (Total prestige first, then level, then experience and lastly by the oldest update timestamp)
The query I use for this is:
SELECT u.id, u.username, u.mode, u.total_prestige as prestige, u.total_level as level, u.total_xp as exp, s.created FROM hs_users u JOIN hs_userskill s ON u.id = s.userId
WHERE s.id IN (SELECT MAX(id) FROM hs_userskill WHERE userId = u.id GROUP BY userId)
ORDER BY total_prestige DESC, total_level DESC, total_xp DESC, created ASC;
But now, on a different page, I need to find the players "rank" (so basically their index in the results list)
Is there a proper sql way of doing this (probably), instead of just taking the whole results set into code and looping over it? As I am tempted at doing at this point in time.
My database structure in sql:
CREATE TABLE IF NOT EXISTS `hs_modes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`modename` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`modeicon` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `hs_skills` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`skillname` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`skillicon` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `hs_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL UNIQUE,
`mode` int(11) NOT NULL DEFAULT '0',
`total_prestige` int(11) NOT NULL DEFAULT '0',
`total_level` int(11) NOT NULL DEFAULT '0',
`total_xp` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
FOREIGN KEY (`mode`) REFERENCES `hs_modes`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `hs_userskill` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) NOT NULL,
`skillId` int(11) NOT NULL,
`prestige` int(11) NOT NULL,
`experience` int(11) NOT NULL,
`created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FOREIGN KEY (`userId`) REFERENCES `hs_users`(`id`),
FOREIGN KEY (`skillId`) REFERENCES `hs_skills`(`id`),
UNIQUE KEY `userskill` (`userId`, `skillId`, `prestige`, `experience`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
In mysql 8 you have ROW_NUMBER to signify the position in the specified order
SELECT
u.id,
u.username,
u.mode,
u.total_prestige AS prestige,
u.total_level AS level,
u.total_xp AS exp,
s.created
,ROW_NUMBER() OVER(ORDER BY total_prestige DESC , total_level DESC , total_xp DESC , created ASC) rn
FROM
hs_users u
JOIN
hs_userskill s ON u.id = s.userId
WHERE
s.id IN (SELECT
MAX(id)
FROM
hs_userskill
WHERE
userId = u.id
GROUP BY userId)
ORDER BY total_prestige DESC , total_level DESC , total_xp DESC , created ASC;
Fro Bob rank
SELECT
*
FROM
(SELECT
u.id,
u.username,
u.mode,
u.total_prestige AS prestige,
u.total_level AS level,
u.total_xp AS exp,
s.created
,ROW_NUMBER() OVER(ORDER BY total_prestige DESC , total_level DESC , total_xp DESC , created ASC) rn
FROM
hs_users u
JOIN
hs_userskill s ON u.id = s.userId
WHERE
s.id IN (SELECT
MAX(id)
FROM
hs_userskill
WHERE
userId = u.id
GROUP BY userId)
) t1
WHERE username = 'Bob'
Set the database connection cursor to fetch the result as dictionary, then you can access the data as dictionary index of it's column names
I have a ledger table, and right now I have the ability to find the date or NULL if someone is delinquent based on their payment history. I need a query that allows me to find all delinquent members instead of just a specific one.
I need the ability to run a query that gets any member that is delinquent and return to me the member_id and the date of delinquency.
Basically what the original query to find delinquency for a specific member does, just doing every member instead of a specific one.
I have tried:
SELECT DISTINCT member_id, created_at FROM member_ledger_items WHERE
balance > 0 and id > (
IFNULL(
(SELECT id from member_ledger_items WHERE balance <= 0 and member_ledger_items.deleted_at is NULL GROUP BY member_id ORDER BY created_at, id desc LIMIT 1),
0
)
) and `member_ledger_items`.`deleted_at` is null GROUP BY member_id order by created_at asc, id asc;
This is the query to find if a specific member is delinquent:
select `created_at` from `member_ledger_items` where `member_id` = ? and `balance` > 0 and `id` >
(
IFNULL(
(select `id` from `member_ledger_items` where `member_id` = ? and `balance` <= 0 and `member_ledger_items`.`deleted_at` is null order by `created_at` desc, `id` desc limit 1)
, 0)
)
and `member_ledger_items`.`deleted_at` is null order by `created_at` asc, `id` asc limit 1;
Here is the create syntax of the member_ledger_items table:
CREATE TABLE `member_ledger_items` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`member_id` int(10) unsigned NOT NULL,
`status` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`type` enum('credit','debit') COLLATE utf8_unicode_ci NOT NULL,
`category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`memo` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`amount` decimal(13,3) DEFAULT NULL,
`autopay` tinyint(1) DEFAULT NULL,
`late` tinyint(1) DEFAULT NULL,
`date` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`balance` decimal(13,3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=53596 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
I need rows with member_id and date of starting delinquency.
Is this possible?
Any help would be appreciated!
SELECT `member_id`,
(SELECT `created_at`
FROM `member_ledger_items` AS MLI2
WHERE `balance` > 0
AND MLI2.`member_id` = MLI.`member_id`
AND `id` > ( Ifnull((SELECT `id`
FROM `member_ledger_items` AS MLI3
WHERE `balance` <= 0
AND MLI3.`member_id` =
MLI2.`member_id`
AND MLI3.`deleted_at` IS NULL
ORDER BY `created_at` DESC,
`id` DESC
LIMIT 1), 0) )
AND MLI2.`deleted_at` IS NULL
ORDER BY `created_at` ASC,
`id` ASC
LIMIT 1) AS created_date
FROM `member_ledger_items` AS MLI
GROUP BY `member_id`;
Ended up being the solution
i have records that have value equal to 1 or 2 i want to order rows by this record like this
1
2
1
2
1
2
1
how can i do this order by only orders by DESC or ASC ?
note : there is a primary key id, and there is a created date
CREATE TABLE `users` (
`uid` INT(11) NOT NULL AUTO_INCREMENT,
`phone_number` VARCHAR(16) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`first_name` VARCHAR(100) NOT NULL,
`last_name` VARCHAR(100) NOT NULL,
`sex` TINYINT(4) NOT NULL, << this will have only 1 or 2
`created_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`account_status` TINYINT(1) NOT NULL,
PRIMARY KEY (`uid`)
);
Try this query:
SELECT t.*
FROM (
SELECT STRAIGHT_JOIN uid, first_name ,
#y:=IF(#sex=sex,#y+1,1) AS rnk, #sex:=sex AS sex
FROM (SELECT #sex:=NULL) AS x
JOIN users
ORDER BY sex, uid DESC
) AS t
ORDER BY rnk ASC, sex ASC;
I have problem with sql query. The idea is to select all loans that are after payment (status 1/2/3) between 8 and 21 days with calculated value from payment_day til now.
I have already done some query but can't use columns days_after_payment and days_after_part_payment in WHERE section. I would like to have one column like days_after_payment based on loan type.
SELECT l.*,
(SELECT SUM(`value`) FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0) AS left_to_pay,
-(DATEDIFF((SELECT date FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0 AND `date`<CURDATE() ORDER BY `date` LIMIT 1), NOW())) AS days_after_part_payment,
-(DATEDIFF(l.payment_date, NOW())) AS days_after_payment
FROM loan l
WHERE (l.type=1 or l.type=2) AND (l.status=1 OR l.status=2 OR l.status=3)
GROUP BY l.client_id
ORDER BY
CASE l.type
WHEN 1 THEN days_after_payment
WHEN 2 THEN days_after_part_payment
ELSE 1 END
ASC
CREATE TABLE IF NOT EXISTS `loan` (
`id` int(11) NOT NULL,
`value` int(11) NOT NULL,
`client_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
`type` int(11) NOT NULL,
`payment_date` date DEFAULT NULL
) ENGINE=MyISAM AUTO_INCREMENT=2068 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `loan_part` (
`id` int(10) unsigned NOT NULL,
`loan_id` int(11) NOT NULL,
`value` float NOT NULL,
`date` date DEFAULT NULL,
`paid` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=MyISAM AUTO_INCREMENT=1751 DEFAULT CHARSET=utf8;
Update1 : I had to cut unnecessary columns and rewrite it into English from my native language.
ORDER BY 7
"7" means the 7th field in the SELECT. That works for GROUP BY also. I had to see the table definition to count how many in l.*.
How come id is not declared AUTO_INCREMENT?
I want to fetch latest 3 news from each news type.
CREATE TABLE IF NOT EXISTS `news` (
`news_id` int(8) NOT NULL AUTO_INCREMENT,
`news_heading` tinytext NOT NULL,
`news_description` text NOT NULL,
`news_date` date DEFAULT NOT NULL,
`news_type` tinyint(1) NOT NULL COMMENT '0- PEP|1 - MEDIA|2 - CONSULTING',
`created_date` datetime NOT NULL,
`modified_date` datetime NULL,
`display` tinyint(1) NOT NULL COMMENT '0- ON | 1 -OFF',
PRIMARY KEY (`news_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
Below Query will give me only 1 latest news from all type. Suggest me how we can achieve for top 3 from each type
SELECT * FROM (
SELECT * FROM `news`
ORDER BY `created_date` DESC
) AS TBL
GROUP BY `news_type`
Try this:
SELECT news_id, news_heading, news_description, news_date,
news_type, created_date, modified_date, display
FROM (SELECT news_id, news_heading, news_description, news_date,
news_type, created_date, modified_date, display,
IF(#news_type = #news_type:=news_type, #id:=#id+1, #id:=1) AS id
FROM news, (SELECT #id:=1, #news_type:=0) A
ORDER BY news_type, created_date DESC
) AS A
WHERE id <= 3;