Archive of

Understanding `COUNT`, `LAST`, `FIRST`, and `NEXT` in PL/SQL Collections

TL;DR

In PL/SQL, COUNT, LAST, FIRST, and NEXT are essential for handling collections, especially if there are gaps (unpopulated indexes). Here’s a quick summary:

  • COUNT: Gives the number of populated entries.
  • LAST and FIRST: Identify the highest and lowest populated indexes, respectively.
  • NEXT: Finds the next populated index, skipping gaps automatically—great for sparse collections.

In PL/SQL programming, collections like nested tables, VARRAYs, and associative arrays are powerful tools for handling sets of data. However, understanding how to work with populated and unpopulated indexes can be tricky. That’s where COUNT, LAST, FIRST, and NEXT methods come into play. These methods let you manage and iterate over collections effectively, especially when dealing with gaps (unpopulated indexes). Let’s look at each one and how to use it.

1. COUNT

  • Purpose: Returns the number of populated elements in a collection.
  • Use Case: COUNT is useful when you need the exact count of entries in a collection, especially for fully populated collections without gaps.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER;
  v_numbers NumberTable := NumberTable(1, 2, 3, 4, 5); -- Fully populated
BEGIN
  DBMS_OUTPUT.PUT_LINE('COUNT: ' || v_numbers.COUNT);  -- Outputs: 5
END;

2. LAST

  • Purpose: Returns the highest populated index in the collection, whether all indexes are filled or not.
  • Use Case: When you need to find the highest valid index, such as for looping through all possible entries.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
BEGIN
  v_numbers(1) := 10;
  v_numbers(3) := 30; -- Gap at index 2
  v_numbers(5) := 50;

  DBMS_OUTPUT.PUT_LINE('LAST: ' || v_numbers.LAST);  -- Outputs: 5
END;

3. FIRST

  • Purpose: Returns the lowest populated index in the collection.
  • Use Case: Similar to LAST, FIRST is used when you want to start iterating from the lowest populated index.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
BEGIN
  v_numbers(2) := 20;
  v_numbers(4) := 40;

  DBMS_OUTPUT.PUT_LINE('FIRST: ' || v_numbers.FIRST);  -- Outputs: 2
END;

4. NEXT

  • Purpose: Given an index, NEXT returns the next highest populated index. If there are no more populated indexes after the current one, it returns NULL.
  • Use Case: NEXT is perfect for iterating only over populated elements, skipping gaps automatically.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
  v_index PLS_INTEGER;
BEGIN
  v_numbers(1) := 10;
  v_numbers(3) := 30;
  v_numbers(5) := 50;

  v_index := v_numbers.FIRST;
  WHILE v_index IS NOT NULL LOOP
    DBMS_OUTPUT.PUT_LINE('Value at index ' || v_index || ': ' || v_numbers(v_index));
    v_index := v_numbers.NEXT(v_index);
  END LOOP;
END;

Output:

Value at index 1: 10
Value at index 3: 30
Value at index 5: 50

Choosing the Right Method

  • COUNT is ideal for knowing how many elements are populated.
  • LAST and FIRST are great for determining the range of populated indexes.
  • NEXT is best when you want to loop through only populated elements, especially if there are gaps.

Key Takeaways

Using COUNT, LAST, FIRST, and NEXT effectively allows you to handle PL/SQL collections with gaps, optimize loops, and avoid errors when accessing unpopulated indexes. For sparse collections (with gaps), FIRST and NEXT are preferred over looping with 1..LAST to ensure you only interact with valid, populated indexes.

Best Practice for Gapped Collections

When working with collections that might have gaps, avoid using 1..COUNT or 1..LAST directly in loops, as they may access unpopulated indexes and cause errors. Instead:

  1. Use FIRST and NEXT to loop through only populated elements:

    • Start with v_index := v_collection.FIRST and use v_index := v_collection.NEXT(v_index) to move to the next populated index.
    • This skips gaps automatically, making it the most efficient way to handle sparse collections.
  2. Add NULL Checks if Necessary:

    • Even with FIRST and NEXT, you can add an IF condition to verify a non-null value for specific fields if you expect incomplete records.

Example:

DECLARE
  TYPE MyCollection IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_collection MyCollection;
  v_index PLS_INTEGER;
BEGIN
  v_collection(1) := 10;
  v_collection(3) := 30;
  v_collection(7) := 70;

  v_index := v_collection.FIRST;
  WHILE v_index IS NOT NULL LOOP
    DBMS_OUTPUT.PUT_LINE('Value at index ' || v_index || ': ' || v_collection(v_index));
    v_index := v_collection.NEXT(v_index);
  END LOOP;
END;

This approach ensures your code only processes valid elements, skipping over unpopulated entries smoothly.

移动介质上的Linux:使用 tmpfs、关闭 atime 以延长 SD 卡或优盘的使用寿命

我有一台老旧的 Asus C100P Chromebook,Google早在几年前就停止了对它的支持。它是32位ARM CPU,因此我没法替它更换BIOS,但我可以在developer mode下启用优盘启动,这样我就能够在优盘或者sd卡运行Linux。

然而,在使用 SD 卡或其他闪存设备作为操作系统存储时,合理管理写入操作至关重要。本文将介绍如何通过使用 tmpfs 文件系统、关闭 atime 来延长设备的使用寿命。

1. 使用 tmpfs

什么是 tmpfs?

tmpfs 是一种基于内存的临时文件系统,具有以下优点:

  • 高速存储: 数据存储在内存中,读写速度极快。
  • 动态大小: 根据实际使用情况动态分配内存,不会固定占用资源。
  • 减少写入: 适合存储临时文件和日志,显著减少对闪存的写入操作。

如何配置 tmpfs

1.1 编辑 /etc/fstab 文件

打开终端并输入:

sudo nano /etc/fstab

添加以下行以创建 tmpfs 挂载点:

tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0
tmpfs /var/log tmpfs defaults,noatime,mode=0755 0 0

1.2 挂载 tmpfs

保存文件后,运行以下命令使更改生效:

sudo mount -a

1.3 验证挂载

使用以下命令确认 tmpfs 是否成功挂载:

df -h

您应该能看到类似于 /tmp/var/log 的 tmpfs 挂载点。

1.4 设置自动复制日志到 tmpfs

为了在系统启动时自动将日志复制到 tmpfs,我们需要修改 /etc/rc.local 文件:

a. 创建一个目录来存储持久化的日志:

sudo mkdir -p /var/log.hdd
sudo cp -a /var/log/* /var/log.hdd/

b. 编辑 /etc/rc.local 文件:

sudo nano /etc/rc.local

c. 在文件中添加以下内容:

#!/bin/sh -e
# 复制日志文件到 tmpfs
cp -a /var/log.hdd/* /var/log/
exit 0

d. 确保 rc.local 文件具有执行权限:

sudo chmod +x /etc/rc.local

2. 关闭 atime

atime (访问时间) 是文件系统的一个属性,每次访问文件时都会更新。关闭 atime 可以减少不必要的写入操作,从而延长 SD 卡的寿命。

如何关闭 atime

2.1 编辑 /etc/fstab 文件

打开 /etc/fstab 文件:

sudo nano /etc/fstab

2.2 修改挂载选项

找到 SD 卡对应的挂载项(通常是根分区 /),在挂载选项中添加 noatime:

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx / ext4 defaults,noatime 0 1

2.3 重新挂载文件系统

保存文件后,使用以下命令重新挂载文件系统:

sudo mount -o remount /

请记住,虽然这些方法可以有效延长 SD 卡的使用寿命,但仍然建议定期备份重要数据,以防意外发生。毕竟备份不做,十恶不赦!

又:

mode=1777 中的 1 表示设置了"粘滞位"(sticky bit)。具体含义如下:

  1. 777 部分:

    • 7: 所有者(owner)有读、写、执行权限
    • 7: 用户组(group)有读、写、执行权限
    • 7: 其他用户(others)有读、写、执行权限
  2. 前面的 1:

    • 表示设置了粘滞位(sticky bit)

粘滞位的作用:

  • 对于目录,当设置了粘滞位时,只有文件的所有者、目录的所有者或 root 用户才能删除或重命名该目录中的文件。
  • 这通常用于像 /tmp 这样的公共目录,允许所有用户创建文件,但防止用户删除或修改其他用户的文件。

所以 mode=1777 的含义是:

  • 所有用户都可以在该目录中创建、读取和执行文件(777)
  • 但只有文件所有者和目录所有者可以删除或重命名文件(1)

这种权限设置既保证了目录的共享性,又提供了一定的安全保护,防止用户互相干扰。

网友语录 - 第2期

世界上没有叛逆的孩子,只有不被理解的孩子。

点赞订阅虽然好 自由思考价更高

别人喜欢你,那是你给他的福利,别人不喜欢你,那是你给他的惩罚。哈哈哈记住了。

产品是丰富的,营销更是过量的。比起研究多少购物攻略,更核心的步骤是确定自己的需求和预算,越明确越坚持越好。“筛选”比“比较”更简单。

朱丽叶 名字有那么重要吗? 把玫瑰叫成别的名字,它还是有芬芳香气。

不要浪费你的无知。当你对一门知识毫无所知时,最容易问出简单而重要的问题。 一旦你习惯了某种现象或事物,认为它是理所当然时,思维就不知不觉受到了限制。 -- David Kohn

ayaduji:恨是很没意义的事,因为大家都会死。还是抓紧时间爱吧,可爱的东西那么多,时间根本不够啊

如果你无须转译就能直接理解另一种语言及构筑其上的世界观,能欣赏它们的效用与美感,那么你往往就不那么容易偏执盲从,不那么容易把非你族类全都看作妖魔鬼怪。《语言塑造人类思维》

性别代词特别容易产生隐含的联想,影响人们对自己和他人的认知。

拥有另一种语言,就是拥有另一个灵魂。 ——查理大帝

旧时明月照无眠 人总是透过语言的lens在观察/理解世界,因此多掌握一门语言也意味着多一个不同的看世界的lens,而这是翻译软件或者AI所无法做到的,因为翻译完成的那一刻,原始语言的维度就坍塌了。人使用翻译软件/AI是problem-solving,而人学习语言是generative的。反过来,禁止使用原本的语言一向是settler colonialism的assimilation agenda上最高优先级的任务之一。

旧时明月照无眠 精力/energy并不是homogenous,而是由许多适配不同活动/task的不同种类精力组合在一起的。比如每天有一些可以全神贯注的精力,也有一些“按部就班/autopilot”的精力,有些事情只能用专注精力去处理,但有些可以fit进autopilot精力里;然而即便是专注精力也未必能全部用来做比较重要的decision-making,但消耗完了decision-making的容量可能还剩一些可以探索/学新东西的或者是做planning的。

...

精力这个看似和精神更相关的东西其实和肉体的关系也许比想像得更紧密。除了身体状态好会精力更充足之外,最近发现精力的使用和体力运动很像:每天有上限,超过上限去用会伤(接下来几天得“躺平”,否则积累下来就会burn-out),但不使用/锻炼精力“肌肉”又会让每天休息后再生出来的精力变少(某种意义上的用进废退),然而人又是有极限的,并不是锻炼就能无限“长肌肉”。

我发现纸质/电子笔记已经可以很有效分担大量的记忆saving & loading了,因为光是“记住在脑子/working memory里但暂时不(能)执行”已经是在消耗精力了(e.g. 女性们经常抱怨的看似家里男性干活了但还是需要自己去记着什么时候该干什么活因此累得不行却仿佛什么都没干)。

我感觉任务切换的时候感觉翻笔记比纯凭脑子回忆之前停在哪里对精神的taxing要少。以及笔记并不只局限在todo list,完全可以搭更复杂的结构去支持更多externalization(我用logseq,有象友推荐过obsidian)。比如目前我把厨房里的事几乎大部分都externalize给了笔记:架子上和冰箱里的存货/购买or开封时间、某种食物能在冰箱里放的最长时限、某种食材的平均消耗速度(用来估算下次要买大概是什么时候)、不同用具的上一次深度清洁时间、爱吃的菜谱库(按菜式分但可以按食材检索,对于买最小包装食材也一次用不完的救星)、买的大包装食材的使用规划&近期的meal planning(我不太meal prep,但是有一定planning不需要每顿现场想吃什么可以直接拎刀做对我很重要)……这样做的好处一来是脑子需要记的东西和事大量减少,二来是decision-making变少+变简单了(“摊开在眼前”迅速提供了可行方案或者铲除了不可行的,比在脑子里想容易),各种精力消耗得都少了。

更多 RSS

How to Enable User-Level Systemd Services to Start Automatically on Ubuntu After Reboot

TL;DR

If your user-level systemd service on Ubuntu doesn't start automatically after a reboot, enable it with systemctl --user enable HappyNotes.Api.service. If you encounter an error about an existing symlink, remove it first. To allow the service to run without an active user session, enable lingering using loginctl enable-linger <username>. Finally, ensure your service file has the correct [Install] section and reboot to check if the service starts as expected.

The Problem

After configuring a user-level service with systemd, you might find that it remains inactive after rebooting your server. For example, you may run the command:

systemctl --user status HappyNotes.Api.service

And see output indicating that the service is inactive (dead) and disabled.

Solution Steps

  1. Enable the Service: First, ensure your service is enabled to start at boot:

    systemctl --user enable HappyNotes.Api.service
    

    If you encounter an error stating that the service is already linked, you may need to remove the existing symlink:

    rm ~/.config/systemd/user/default.target.wants/HappyNotes.Api.service
    
  2. Check for Linger: User-level services require an active user session to run. To allow your user services to run even when you're not logged in, enable lingering:

    loginctl enable-linger $USER
    
  3. Verify Service Configuration: Ensure your service file has the correct [Install] section:

    [Install]
    WantedBy=default.target
    
  4. Reboot and Test: After enabling lingering and ensuring your service is set up correctly, reboot your server:

    sudo reboot
    

    After rebooting, check the status of your service again:

    systemctl --user status HappyNotes.Api.service
    

By following these steps, you can ensure that your user-level systemd services start automatically after a reboot on Ubuntu. Enabling lingering is the key, which is particularly useful for server environments where continuous operation of services is desired.

Docker Volumes in Production: A Practical Guide to Named Volumes vs Bind Mounts

When working with Docker containers in production, understanding volume management is crucial. This guide will help you make informed decisions about when to use named volumes versus bind mounts (directory mapping).

TL;DR

  • Use named volumes for persistent data (databases, application state)
  • Use bind mounts for config files and development
  • Combine both in production for optimal setup

Understanding the Basics

Named Volumes

volumes:
  - postgres_data:/var/lib/postgresql/data

Docker manages these volumes internally. Think of them as "black boxes" that Docker handles for you.

Bind Mounts (Directory Mapping)

volumes:
  - ./config:/etc/app/config

You manage these directories directly on your host machine.

When to Use What?

Use Named Volumes For:

  1. Database storage
  2. Application state
  3. Generated assets
  4. Any data that needs persistence but not direct access

Benefits:

  • Managed by Docker
  • Better performance
  • Automatic permissions handling
  • Easier backups
  • Portable across environments
  • Built-in volume management commands

Use Bind Mounts For:

  1. Configuration files
  2. Static files during development
  3. Source code in development
  4. Any files you need to edit from host

Benefits:

  • Direct access from host
  • Easy to edit
  • Version control friendly
  • Quick updates without container restart
  • Shareable across environments

Real-World Example

Here's a typical production setup combining both approaches:

version: '3'
services:
  db:
    image: postgres:15
    volumes:
      # Data persistence with named volume
      - postgres_data:/var/lib/postgresql/data
      # Configuration with bind mounts
      - ./config/postgres.conf:/etc/postgresql/postgresql.conf:ro

  nginx:
    image: nginx
    volumes:
      # Config files with bind mounts
      - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./config/nginx/conf.d:/etc/nginx/conf.d:ro
      # Static files with bind mount
      - ./static:/usr/share/nginx/html:ro

  app:
    image: node:18
    volumes:
      # Application data with named volume
      - app_data:/app/data
      # Config with bind mount
      - ./config/app.json:/app/config/app.json:ro

volumes:
  postgres_data:
  app_data:

Migration Tips

Moving from Bind Mounts to Named Volumes

If you're currently using bind mounts for data and want to switch to named volumes:

# Create new volume
docker volume create myapp_data

# Copy data
docker run --rm \
  -v /old/path:/source:ro \
  -v myapp_data:/destination \
  ubuntu \
  bash -c "cp -av /source/. /destination/"

Best Practices

  1. Named Volumes

    • Always use for persistent data
    • Name them descriptively
    • Regular backups
    • Don't manipulate directly on host
  2. Bind Mounts

    • Use read-only (:ro) when possible
    • Keep configs in version control
    • Use relative paths for portability
    • Store in a config/ directory
  3. General

    • Document your volume strategy
    • Regular backups for both types
    • Monitor disk usage
    • Use clear naming conventions

Common Pitfalls to Avoid

  1. Using bind mounts for database storage
  2. Hardcoding absolute paths
  3. Not setting proper permissions
  4. Forgetting to backup named volumes
  5. Direct manipulation of named volume directories

Conclusion

The key to successful Docker volume management is using the right tool for the job:

  • Named volumes for data that needs persistence
  • Bind mounts for configs and development
  • Combine both for a robust production setup

Remember: When in doubt, prefer named volumes for data and bind mounts for configuration.