Featured

MySQL get missing IDs

เคสแรกหล่ะกัน กรณีกำหนด Key เป็น Auto increment ใน MySQL แล้วเลขลำดับ/Key/IDs บางตัวหายไป (หลายสาเหตุ) ลองมาทำ Dummy Table ทดสอบดูกัน

CREATE TABLE numbers(
  startNumber INTEGER UNSIGNED,
  PRIMARY KEY (`startNumber`)
);
ALTER TABLE numbers ADD INDEX idx1 (startNumber);

INSERT INTO numbers VALUES
(1),(2),(3),(4),(5),(6),(8),(9),(20),(21),(22),(53),(54),
(71),(72),(74),(80);

จากตารางดัมมี่ข้างต้นผลลัพธ์ที่เราต้องการคือตัวเลข 7, 10-19, 23-52, 55-70, 73, 75-79 

ลองเขียนคำสั่งเพื่อให้ได้คำตอบด้านบนหลายวิธี วิธีแรกต้องใช้ตาราง Sequence Number ใน MySQL อาจต้องทำ Manual ไปก่อน

วิธีแรก

CREATE TABLE seq (`seqno` INTEGER UNSIGNED, PRIMARY KEY (`seqno`));
INSERT INTO `seq` VALUES (0),(1),(2),(3),(4),(5),(6),(7);


INSERT INTO `seq` SELECT seqno+8 from `seq`;
INSERT INTO `seq` SELECT seqno+16 from `seq`;
INSERT INTO `seq` SELECT seqno+32 from `seq`;
INSERT INTO `seq` SELECT seqno+64 from `seq`;
INSERT INTO `seq` SELECT seqno+128 from `seq`;
INSERT INTO `seq` SELECT seqno+256 from `seq`;
INSERT INTO `seq` SELECT seqno+512 from `seq`;

ตามด้วยใช้วิธีการ Left Join

SELECT
  s.*
FROM seq s 
  LEFT JOIN numbers n ON s.seqno = n.startNumber
WHERE n.startNumber IS NULL 
AND s.seqno < (SELECT MAX(startNumber) FROM numbers);

วิธีที่ 2

SELECT nstart, 
       nend
FROM (SELECT m.startNumber + 1 AS nstart,
              (SELECT MIN(startNumber) - 1 
                 FROM numbers x 
                 WHERE x.startNumber > m.startNumber) AS nend
          FROM numbers m 
                LEFT JOIN
                   (SELECT startNumber-1 startNumber 
                      FROM numbers r) r 
                ON (m.startNumber = r.startNumber)
         WHERE r.startNumber IS NULL
       ) x
WHERE nend IS NOT NULL
ORDER BY nstart;

วิธีที่ 3 ใช้ NOT IN 

SELECT
  s.*
FROM seq s 
WHERE s.seqno NOT IN (SELECT startNumber FROM numbers) 
AND s.seqno < (SELECT MAX(startNumber) FROM numbers);

วิธีที่ 4 ใช้ GROUP BY เพื่อใช้ SQL aggregate functions

SELECT a.startNumber+1 AS start, MIN(b.startNumber) - 1 AS end
FROM numbers AS a, numbers AS b
WHERE a.startNumber < b.startNumber
GROUP BY a.startNumber
HAVING start < MIN(b.startNumber);

ผลลัพธ์ที่ได้ก็ไปลองเล่นตามนี้ ได้เลย http://sqlfiddle.com/#!9/bbc3e7/3


ท้ายทีสุดแล้วมันอาจมีวิธีการที่มากกว่านี้แหล่ะ ผลลัพธ์ก็ได้ได้เช่นกันแต่ความแตกต่างคือ Big O คนซีเรียสก็ควรซีเรียส 5555

Featured

How to apply blur effect in GNOME 40

ว่าด้วยเรื่องเอฟเฟคความเบลอใน Gnome Desktop นี่น่าจะเป็นเรื่องที่ถามกันบ่อย ๆ พอสมควรในบอร์ดต่างประเทศตั้งแต่ที่อีกฝั่งเช่น

  • Windows ก็มี Aero Glass ไล่มาจนถึง Fluent design/Acrylic effect ใน Windows 10
  • หรือฝั่ง Mac ที่มี Blue Effect
  • รวมถึงฝั่ง KDE เองที่ใช้ Blur Effect ได้ใน Plassma Theme จนแล้วจนรอดก็ยังไม่มีใน GNOME ^__^

ช้าก่อนใช่ว่า GNOME Desktop เองจะไม่สามารถทำได้เลยซะทีเดียว ก่อนหน้านี้มีตัว Compiz เองซึ่งมันก็พอทำได้แหล่ะ แต่ด้วยหลัง ๆ ที่โดนถอดออก ท่าที่ใช้ก็เลยยุ่งยากขึ้นมาหน่อย แต่พอเจอ Windows shadows causing artefacts with the new Shell.BlurEffect (Gnome 3.36.2, both Xorg and Wayland) ก็มี Gnome Extension ที่ใช้ประโยชน์จากตรงนี้ ทำให้การใช้งาน Blur Effect ง่ายขึ้น มี Extension อยู่ 2 ตัว คือ

แต่ตัวที่เราจะใช้งานเพื่อให้ Blur Effect มีผลกับทุกหน้าต่างด้วยก็คือ Blur me (fork จาก Blur my Shell นั่นแหล่ะ) มี 2 วิธีให้ใช้งานคือ

วิธีที่ 1 (ตามคำแนะนำของ Extension เอง)

  1. ติดตั้ง Extension  Blur me  และก็เปิดใช้งาน
  2. เปลี่ยนไปใช้ Theme Materia Transparent
  3. ซึ่งผลลัพธ์ที่ได้

วิธีที่ 2 (Tweak นิดนึง)

  1. ติดตั้ง Extension  Blur me  และก็เปิดใช้งานเหมือนกัน
  2. ด้วยความที่ Extension Blur me เองมีผลกับความโปร่งแสง (Transparency) ของ Theme เราจึงสามารถ
      1. ติดตั้ง Extension เพิ่มคือ Glassy GNOME
      2. ติดตั้ง Application devilspie และสร้างสคริปท์ ชื่อ opacity.luaเพิ่มที่$HOME/.config/devilspie2 ดังนี้
        local current_app = get_application_name()
        local current_window_type = get_window_type()
        
        set_window_opacity(0.69)
        debug_print("Application: " .. get_application_name())
        debug_print("Window: " .. get_window_name() .. " Window Type: " .. get_window_type());		
        
  3. ถ้าเลือก 2.1 ก็ให้เปิดใช้งานทั้ง 2 Extension หรือถ้าเลือก 2.2 ก็เพิ่มให้ devilspie ทำงานตอน Startup หรือเปิดโปรแกรมทำงานเมื่อต้องการ
  4. วิธีนี้มีผลกับทุก Theme เผื่อไม่ชอบธีม Materia ^_^ ผลลัพธ์ที่ได้ก็

จบปิ๊ง !!! ^_^

ป.ล.

  • Extension Blur me เคลมว่าใช้ 3% CPU usage
  • Extension Blur me ยังมีปัญหากับ Dash to dock ผู้เขียนเลยปิดการใช้งานส่วนนี้ไป
  • เครื่องผู้เขียนใช้งาน Ubuntu 21.04 จากการใช้งานจริงก็มีอาการแล็กให้เห็นบ้างนะ กรณีใช้ไปนาน ๆ

How to run chrome extension with a Desktop shortcut in Ubuntu

กรณีที่เราจะ Run Chrome Extension ที่ติดตั้งไว้ใน Browser สามารถรันผ่าน Command Line ใน Terminal สามารถรันแบบนี้ได้เลย

brave-browser --app=chrome-extension://<Extension ID>

เช่นต้องการรัน Line Application ก็จะสั่งแบบนี้

brave-browser --app=chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc/index.html

ที่นี้ใน Ubuntu เองเราสามารถสร้าง Desktop shortcut launcher ได้เพื่อรันโปรแกรมได้ (ปกติจะเก็บไว้ที่ /usr/local/share/applications หรือ ~/.local/share/applications)

ตัวอย่างไฟล์ .desktop ของ Line ที่เป็น Chrome Extension กัน

[Desktop Entry]
Version=1.0
NoDisplay=false
Name=Line
Comment=Line chrome extension for brave browser
GenericName=Line
Keywords=Line
Exec=brave-browser --app=chrome-extension://ophjlpahpchlmihnnnihgmmeilfjmjjc/index.html
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=brave-browser
Categories=GNOME;GTK;Network;
StartupNotify=true
StartupWMClass=Code
Encoding=UTF-8
X-Desktop-File-Install-Version=0.26

 


จบปิ๊ง

 

Run containers with Podman on WSL2 Part 1

Part 1  (แพลนมี Part 2 แหล่ะ จะพยายามมีวินัยมาบล็อกเก็บไว้ ^_^)

เบื้องต้นคือ Windows เองต้องรองรับและเปิด WSL2 ขั้นตอนการเปิดก็ตามนี้
1. Enable Windows Subsystem for Linux (WSL)

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

2. Enable Windows Virtual Machine Platform

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

3. อัพเดต Linux kernel to the latest version ดาวน์โหลดแพคเกจ

4. กำหนดให้ WSL2 เป็นค่า Default

wsl --set-default-version 2

5. ติดตั้ง Linux ดิสโทรที่ต้องการ สามารถเลือกได้จาก Store หรือ List ตัวที่ออนไลน์พร้อมติดตั้งโดยใช้คำสั่ง

wsl --list --online

5.1 แต่เราจะเลือก Alpine Distro แทนใน Part 1 นี้ ไปดูรายละเอียดของ Distro นี้กันได้ที่นี่ ซึ่งมีคนทำไว้สำหรับ WSL2 แล้ว

https://github.com/yuk7/AlpineWSL

ดาวน์โหลดตัวเวอร์ชั่น 3.15 และคลิกที่ไฟล์ Alpine.exe เพื่อติดตั้งได้เลย เป็นอันเสร็จสิ้น

ลิสต์ Distro ที่เราได้ติดตั้งไปแล้วด้วยคำสั่ง

wsl --list --verbose

ถ้าติดตั้งสำเร็จจะเห็นรายการ Distro “Alpine” ก็เป็นอันเสร็จสิ้น

6. ต่อไปจะเป็นการใช้งาน Alpine ให้เปิด PowerShell และเลือก Distro Alpine ตามนี้

wsl -d Alpine

7. ให้ Update package และเริ่มการติดตั้ง podman ตามนี้

apk update && apk add podman

มีแพคเกจ podman-compose (ใช้แทน docker-compose) ที่ต้องติดตั้งเพิ่มเติม ซึ่งจะต้องเพิ่ม repository เข้าไป และต่อด้วยการติดตั้ง แพคเกจตามลำดับ

echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
apk update && apk add podman-compose@testing

เมื่อติดตั้งเรียบร้อย ลองทดสอบการใช้งานด้วยการรัน  compose.yml ไฟล์ตามนี้

version: '3'
services:
  nginx:
    container_name: nginx
    image: docker.io/library/nginx:alpine
    volumes:
      - nginx:/etc/nginx
    ports:
      - 1080:80
      - 1443:443
    tty: true
    restart: unless-stopped
    networks:
      - nginxnetwork
    command: [nginx-debug, '-g', 'daemon off;']

volumes:
  nginx:

networks:
  nginxnetwork:
    name: nginxnetwork
    external: true

จบปิ๊ง ^__^

 

tmux starts up with specified windows and split pane

tmux คือ terminal multiplexer คำอธิบายเต็ม ๆ ก็ตามนี้

tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached.

เวลาเราใช้งานก็ค่อยมาสร้าง Window หรือ Split Horizontal/Vertical ตามใช้งาน สนใจเพิ่มเติมก็อ่านต่อได้ที่นี่ ส่วนต่าง ๆ ของ tmux ตามนี้

แต่ถ้าเรามีแพทเทิร์นใช้งานประจำอยู่แล้วก็สามารถสร้างเป็น Shell script สั้น ๆ ได้ ตัวอย่างเช่น

#!/bin/bash

currentsession=$(tmux display-message -p '#S')
session="$1"

function has-session {
  tmux has-session -t $session 2>/dev/null
}

if has-session ; then
  tmux list-sessions
  read -p "Session already exists. [A]ttach/[K]ill/Kill [S]erver?" -i Y input 
  case $input in  
    a|A)
      # Attach to created session
      tmux attach-session -t $session
      ;;
    k|K)
      tmux kill-session -t $session
      ;; 
    s|S)
      tmux kill-server
      ;; 
    *) echo "What?" ;;
  esac
else
  tmux new-session -d -s $session -n 'home' 'cd ~; bash -i'
  tmux new-window -a -t $session -n 'server' 'bash -i'
  tmux new-window -a -t $session -n 'server-log'  'bash -i'

  tmux select-window -t $session:1 \; split-window -h \; select-pane -t 1 \; split-window -v \; select-pane -t 3 \; split-window -v \; select-pane -t 1 \;
  tmux select-window -t $session:2 \; split-window -h \; split-window -v \; select-pane -t 1 \;
  tmux select-window -t $session:3 \; split-window -h \; split-window -v \; split-window -v \; select-pane -t 2 \; split-window -v \; select-pane -t 1 \;
  
  # Attach to created session
  tmux attach-session -t $session
fi

จะเรียก Shell script เองก็ได้หรือจะสร้างเป็น alias ใน .bashrc/.zshrcตามสะดวก ผลลัพธ์ก็ตามนี้

จบปิ๊ง !!

iperf3 perform network throughput tests บน Docker

iperf คืออะไร

iperf คือ โปรแกรมที่ใช้สำหรับการทดสอบ Network Bandwidth โดยจะเช็คอัตราความเร็วในการรับส่งข้อมูลรวมถึงสามารถวิเคราะข้อมูลพวก Packet loss ต่าง ๆ

เพราะฉนั้น iperf จึงเหมาะสำหรับการนำมาใช้ในการวิเคราะห์เนตเวิร์คภายใน (LAN) การทำงานของ iperf จำเป็นต้องใช้คอมพิวเตอร์อย่างน้อย 2 เครื่องในลักษณะ Server – Client เพื่อทดสอบ มาเริ่มด้วยกันทำ iperf server กัน โดยการสร้าง Dockerfile ตามนี้

FROM alpine

LABEL maintainer mf <[email protected]>

RUN apk add --no-cache iperf3 \
  && adduser -S iperf

USER iperf

EXPOSE 5201/tcp 5201/udp

ENTRYPOINT ["iperf3"]

# iperf3 -s run in Server mode
CMD ["-s"]

และทำการ Build ให้เรียบร้อย

sudo docker build -t mf/iperf3:server .

และทำการ run iperf เพื่อเป็นโหนด Server

sudo docker run --restart=unless-stopped --name=iperf3 -d -p 5201:5201/tcp -p 5201:5201/udp mf/iperf3:server

ที่นี้ก็เริ่มทดสอบกันโดยฝั่ง Client ก็ทำการติดตั้ง iperf3 ให้เรียบร้อย มีรองรับทั้ง Windows macOS iOS Android และ Linux โดย Download ได้ที่นี่  ส่วน Linux ก็ติดตั้งได้โดยใช้คำสั่ง

sudo apt install iperf3

ทดสอบแรกกันด้วย

iperf3 -c <speedtest.mydomain.local>

ลองทดสอบอีกคำสั่ง

iperf3 -R -O 1 -u -b 50M -c speedtest.mydomain.local

สามารถไปดูคำสั่งอื่น ๆ ได้ที่ https://iperf.fr/iperf-doc.php  หรือดูรายละเอียดเรื่อง Bandwidth ได้ที่ https://www.paessler.com/it-explained/bandwidth

จบปิ๊งงง ^__^

 

หาจำนวนรายใหม่โดยใช้การเปรียบเทียบระหว่างแถวใน MySQL

วันนี้บล็อกสั้น ๆ จากหัวข้อไม่งงกันใช่ไหม ^__^  อธิบายคร่าว ๆ คือกรณีเรามีตารางที่เก็บจำนวนสะสมไว้ในแต่ละวัน เวลาเราต้องการทราบว่าแล้ววันนี้มีเพิ่มเข้ามาใหม่เท่าไหร่ โดยทั่วไปทำมือก็อาจจะ

  • นับจำนวนเฉพาะรายที่มาใหม่ไง
  • หรือเอาจำนวนสะสมของวันนี้/ตอนนี้ ไปลบกับวันที่แล้ว

ใช่นั่นแหล่ะทำด้วยมือ แล้วในภาษา SQL เค้าทำยังไงกัน ให้เห็นภาพมากขึ้นเราจะเอาข้อมูลรายงานเคสโควิด 19 ของประเทศไทยในแต่ละวันมาเป็นข้อมูลดัมมี่หล่ะกัน (ข้อมูลจริงนะ นี่  ศบค. รายงานทุกวัน อยากได้ข้อมูลก็ไปที่กรมควบคุมโรค ที่นี่เลย https://covid19.ddc.moph.go.th/th/api)

*เราตัดเฉพาะยอดสะสมของแต่ละวันในเดือนพฤษภาคมหล่ะกัน เพื่อจะได้ง่ายขึ้น

CREATE TABLE `covid19timeline` (
  `ConfirmedDate` date NOT NULL,
  `Confirmed` int(11) NOT NULL,
  `Recovered` int(11) NOT NULL,
  `Hospitalized` int(11) NOT NULL,
  `Deaths` int(11) NOT NULL,
  PRIMARY KEY (`ConfirmedDate`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-01',2960,2719,187,54);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-02',2966,2732,180,54);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-03',2969,2739,176,54);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-04',2987,2740,193,54);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-05',2988,2747,187,54);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-06',2989,2761,173,55);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-07',2992,2772,165,55);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-08',3000,2784,161,55);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-09',3004,2787,161,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-10',3009,2794,159,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-11',3015,2796,163,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-12',3017,2798,163,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-13',3017,2844,117,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-14',3018,2850,112,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-15',3025,2854,115,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-16',3025,2855,114,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-17',3028,2856,116,56);
INSERT INTO `covid19timeline` (`ConfirmedDate`,`Confirmed`,`Recovered`,`Hospitalized`,`Deaths`) VALUES ('2020-05-18',3031,2857,118,56);

ข้อมูลก็ตามนี้

ทีนี้เราก็หาจำนวนรายใหม่กันว่ามีจำนวนเท่าไหร่โดยการเปรียบเทียบกับวันที่แล้ว

SELECT 
	c1.*,
    COALESCE(c1.Confirmed - c2.Confirmed, 0) AS NewConfirmed
FROM covid19timeline c1 
	LEFT JOIN covid19timeline c2 ON c2.ConfirmedDate = DATE_SUB(c1.ConfirmedDate, INTERVAL 1 DAY)
ORDER BY c1.ConfirmedDate;

ผลลัพธ์ที่ได้ตามนี้ โดย NewConfirmed คือจำนวนรายใหม่ที่เพิ่มเข้ามา

จบปิ๊ง !! ^__^

ป.ล.

  • แล้วเราจะเอามาใช้ตอนไหน โดยปกติส่วนใหญ่ก็มักอยู่ในรายงานที่ต้องการดูความเปลี่ยนแปลงเป็นหลักแต่ข้อมูลที่เรามีมักเป็นข้อมูลสะสมอย่างเดียว อย่างเคสการระบาดของโควิด 19 เป็นต้น (แต่ api เค้ามีแจ้งรายใหม่นะตัดออกเฉย ๆ)
  • ไปลองเล่นกันได้ที่ https://www.db-fiddle.com/f/n3Zbf8FmThZxPGDgSGhmVN/0

rsync ห้ามหยุด ห้ามหายและนายต้องไปต่อ

บล็อกต้อนรับต้นเดือนและวันแรงงาน ด้วยจำเป็นต้องย้ายไฟล์จำนวนน้อยนิดมหาศาล แบบเร่งด่วน จำนวนหนึ่งไปไว้อีกทีและงานนี้ก็ห้ามหยุดจนกว่างานจะเสร็จ (นี่มันสะท้อนชีวิตใครบ้างไหม ^__^) ใครมีงานแบบนี้ต้องทำแบบด่วน ๆ และไม่มีเวลาให้ต้องมามอนิเตอร์และรันใหม่อีกรอบ ก็ใช้สคริปท์นี้ไปปรับได้

อธิบายคร่าว ๆ คือ

  • ใช้ rsync ในการโอนย้ายไฟล์ไปอ่านรายละเอียดต่อได้ที่นี่
  • อาจมีบางช่วงหรือ error ขัดจังหวะช่วงโปรแกรม rsync ทำงานแล้วโปรแกรมปิดตัวลงก็ให้โปรแกรม rsync กลับมาทำงานต่อ (Loop) จนกว่าจะสำเร็จ (ดูบังคับมากมาย)
  • เสร็จแล้วก็ Line notify นิดนึง
#!/usr/bin/env bash

# Declare an array of Token
declare -a ACCESS_TOKENS=("token1" "token2")
START=$(date +"%d-%m-%Y %H:%M")
RC=1

while [[ $RC -ne 0 ]]
do
    NOW=$(date +"%d-%m-%Y %H:%M")

    rsync -arv --partial --progress --ignore-errors [source] [destination]
    RC=$?
    
    if [[ $RC -ne 0 ]] ; then
        echo "$NOW Rsync failure. Backing off and retrying..."
        sleep 180
    fi
done

# Iterate the token array
for token in "${ACCESS_TOKENS[@]}"; do
    echo $token
    curl -X POST https://notify-api.line.me/api/notify -H "Authorization: Bearer $token" -F "Rsync start $START completed normally" > /dev/null 2>&1   
done

echo "Rsync start $START completed normally"

จบปิ๊งงง (ไม่ได้ดื้อก็แค่พยายาม)
ป.ล.
เผื่อเคสต้องรีสตาร์ทก็เอาไปไว้ใน cron job ด้วยนะ ^__^

 

Screenshot หน้าเธอแล้วส่งเข้า Line Notify กัน

ปฏิเสธไม่ได้ว่าแอปลิเคชั่นไลน์มีผลอย่างมากกับแวดวงคนทำงาน จะเอกชนหรือวงราชการก็ต้องใช้ แต่ด้วยความที่แอปลิเคชั่นสื่อสารแบบนี้การส่งข้อความยาว ๆ บางครั้งก็เล่นเอาหัวเสียเหมือนกัน เพราะใจความสำคัญมันถูกเผยแพร่แต่เพราะข้อความมันยาว จึงขาดความน่าสนใจ คนรับก็เลื่อนผ่านไปโดยปริยาย (เหมือนเธอที่มองผ่านฉันไป เอ้าเพลงมาาา ซูดดดดด)

รวมทั้งข้อมูลรายงานผลโรค COVID-19 ที่ตอนนี้หลาย ๆ สื่อหลาย ๆ ที่ ได้ทำย่อยข้อมูลเหล่านี้ออกมาเป็น Infographic บ้าง เว็บไซค์แสดง Dashboard บ้าง งั้นจะช้าอยู่ไย เราเซฟรูปเหล่านั้นแล้วส่งเข้าไลน์เลยดีกว่า

Shell Script ไม่ได้มีอะไรซับซ้อนใช้ Headless Chrome ให้ Screenshot หน้าจอตอนนั้นแล้วส่งเข้าไลน์ก็เป็นอันเสร็จสิ้น

#!/bin/bash
  
PICTURE_PATH="./screenshot.png"
NOW=$(date +"%d-%m-%Y %H:%M")
IP=$(hostname -I | awk '{ print $1 }')
DASHBOARD="IP Address"

# Declare an array of Token PHP-CC PSH
declare -a ACCESS_TOKENS=("token1" "token2")

command_exists()
{
  command -v "$1" >/dev/null 2>&1
}

# Take pictures using Chrome Headless
if command_exists google-chrome ; then
    google-chrome --headless --no-sandbox --screenshot --hide-scrollbars --virtual-time-budget=10000 --disable-gpu --window-size=680,380 http://$IP:88/covid
    convert -resize 1280x screenshot.png screenshot-01.png

    google-chrome --headless --no-sandbox --screenshot --hide-scrollbars --virtual-time-budget=10000 --disable-gpu --window-size=1280,1410 http://$DASHBOARD
    mv screenshot.png screenshot-02.png
else
    if command_exists docker ; then 
        sudo docker container run -it --rm -v $(pwd):/usr/src/app zenika/alpine-chrome --no-sandbox --screenshot --hide-scrollbars --virtual-time-budget=10000 --disable-gpu --window-size=680,380 http://$IP:88/covid
        convert -resize 1280x screenshot.png screenshot-01.png

        sudo docker container run -it --rm -v $(pwd):/usr/src/app zenika/alpine-chrome --no-sandbox --screenshot --hide-scrollbars --virtual-time-budget=10000 --disable-gpu --window-size=1280,1410 http://$DASHBOARD
        mv screenshot.png screenshot-02.png
    else
        echo 'Not found docker'
    fi  
fi

convert -append screenshot-01.png screenshot-02.png screenshot.png

# Iterate the token array
for token in "${ACCESS_TOKENS[@]}"; do
    echo $token
    # curl -X POST https://notify-api.line.me/api/notify -H "Authorization: Bearer $token" -F "message=รายงานสถานการณ์ COVID-19 ณ $NOW" -F "imageFile=@$PICTURE_PATH" > /dev/null 2>&1   
done

ผลลัพธ์ที่ได้ก็ตามนี้

จบปิ๊ง บล๊อกเดือนนี้ ^__^
ป.ล.

  • เราใช้ Dashboard ของ อ.เอ แห่ง Developer Mate
  • มี Dashboard 2 ตัว ก็เลย Merge ไฟล์รวมกันซะจะทิ้งไปก็เสียดาย
  • Token ของ Line Notify น่าจะมีกันแล้วนะ อันนี้ข้ามเรื่องการสร้าง Line Notify Token หล่ะกัน
  • ตัวเลขผู้ติดเชื้อวันนี้เห็นแล้วมีแรงทำงานสู้กันต่อ

ลบแถวที่ซ้ำกันใน MySQL

เมื่อมีข้อมูลซ้ำเกิดขึ้นในตารางจะด้วยสาเหตุอะไรก็แล้วแต่ แต่มันมีข้อมูลซ้ำเกิดขึ้นแล้วและเราต้องการลบตัวที่ซ้ำออกไป เราจะทำยังไงมาดูแนวทางกัน

เริ่มต้นเราดูตัวอย่างข้อมูลที่ซ้ำกันก่อน

DROP TABLE IF EXISTS contacts;
 
CREATE TABLE contacts (
    id INT PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL, 
    email VARCHAR(255) NOT NULL
);
 
INSERT INTO contacts (first_name,last_name,email) 
VALUES ('Carine ','Schmitt','[email protected]'),
       ('Jean','King','[email protected]'),
       ('Peter','Ferguson','[email protected]'),
       ('Janine ','Labrune','[email protected]'),
       ('Jonas ','Bergulfsen','[email protected]'),
       ('Janine ','Labrune','[email protected]'),
       ('Susan','Nelson','[email protected]'),
       ('Zbyszek ','Piestrzeniewicz','[email protected]'),
       ('Roland','Keitel','[email protected]'),
       ('Julie','Murphy','[email protected]'),
       ('Kwai','Lee','[email protected]'),
       ('Jean','King','[email protected]'),
       ('Susan','Nelson','[email protected]'),
       ('Roland','Keitel','[email protected]');

มาดูชุดข้อมูลที่ซ้ำกันบ้างว่ามีแถวใดบ้างที่ซ้ำกัน

SELECT 
    email, 
    COUNT(email),
    GROUP_CONCAT(id) AS id_list
FROM contacts
GROUP BY email
HAVING COUNT(email) > 1;


ในแถวที่ซ้ำกันเรา และอยากเก็บไว้แค่ 1 แถว
วิธีที่ 1
ทำการลบด้วยคำสั่งดังนี้

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE c1.id > c2.id AND c1.email = c2.email;

จากคำสั่งข้างบนหมายความว่าเมื่อมีแถวที่ซ้ำกันให้เก็บไว้เฉพาะแถวแรกที่พบ (id ที่ค่าน้อยกว่า) ซึ่งผลลัพธ์ได้ก็ประมาณนี้ (id 6, 12, 13, 14 จะถูกลบไป)

วิธีที่ 2

ALTER IGNORE TABLE contacts 
ADD UNIQUE INDEX idx1(email);

ผลลัพธ์ที่ได้ก็เป็นเช่นคำสั่งด้านบน แต่ขอ หมายเหตุไว้หน่อยเนื่องจาก ALTER IGNORE TABLE ถูกยกเลิกไปแล้วใน MySQL เวอร์ชั่น 5.7.4 เป็นต้นไปวิธีนี้ก็เลยใช้ได้เฉพาะเวอร์ชั่นที่ก่อนหน้าที่ระบุนะครับ

วิธีที่ 3

เนื่องจากหมายเหตุในวิธีที่ 2 ใน MySQL เวอร์ชั่นใหม่ ๆ ก็เลยใช้วิธีแบบนี้แทน

CREATE TABLE contacts_copy LIKE contacts;
ALTER TABLE contacts_copy ADD UNIQUE INDEX idx1(email);

INSERT IGNORE INTO contacts_copy
SELECT * FROM contacts;

DROP TABLE contacts;
ALTER TABLE contacts_copy RENAME TO contacts;

จบปิ๊ง ๆ
ไปลองทดสอบกันได้ที่ https://www.db-fiddle.com/f/sXB3sBDQEdrbdEDFi98Y3j/0
**ส่วนความเจ็บปวดซ้ำ ๆ หน่ะมันลบไม่ได้หรอกนะ