MySQL กับการ JOIN กลุ่มข้อมูลแบบเลือกรายการเดียว ที่ช่วงเวลาเปิดใช้งานสัมพันธ์กัน

โดย SONGCHAI SAETERN
จากบทความ Retrieving the last record in each group จะเป็นการ JOIN แบบเลือกรายการล่าสุด เช่นกรณีที่ข้อมูลในตารางหลัก มีการเปลี่ยนแปลงรุ่น เปลี่ยนเวอร์ชั่น แต่ยังคงใช้รหัสเดิม และต้องการแค่ข้อมูลเวอร์ชั่นล่าสุดเรคอร์ดเดียวเท่านั้น


นอกจากนี้ยังมีอีกกรณี นั่นก็คือ เมื่อเราย้อนกลับไปดูข้อมูลที่เคยบันทึกเอาไว้ โดยอ้างอิงรหัสเดียวกัน ที่ปัจจุบันได้เปลี่ยนเวอร์ชั่นไปแล้ว ข้อมูลที่ JOIN มาจะแสดงอย่างไร??? จะรู้ได้อย่างไรว่า  ณ วันที่นั้นๆ ใช้ข้อมูลเรคอร์ดไหน???

(ถ้าเราเก็บไอดี PK ไปเลยจะไม่เจอปัญหานี้ แต่เนื่องจากฟิลด์ Code ดันเป็น Unique ID ซึ่งยึดเป็น PK ไปในตัวก็เลยคิดว่าไม่จำเป็นต้องสร้าง ID ขึ้นมาอีก)



ตัวอย่างข้อมูลที่เคยบันทึกไว้ด้วยชื่อหมวดหมู่ก่อนการเปลี่ยนแปลง

กรรไกรสองรายการ ที่ใช้หมวดหมู่เดียวกัน แต่คนละปี

กรรไกรตัดกิ่ง   ณ วันที่ 2017-06-01 ได้ทำรายการด้วยชื่อ  "วัสดุทำสวน"
กรรไกรเล็ก       ณ วันที่ 2018-01-31 ได้ทำรายการด้วยชื่อ  "วัสดุอุปกรณ์ทำสวน"

SELECT * FROM product
WHERE name LIKE '%กรรไกร%'

เรามาดูข้อมูลเมื่อ JOIN กับตารางหมวดหมู่กันดู


SELECT product.*,tb_category.name AS cate_name  FROM product
LEFT JOIN tb_category ON product.category_code = tb_category.code
WHERE product.name LIKE '%กรรไกร%'

จะเห็นว่าได้ข้อมูลมาหลายเรคอร์ด และเรคอร์ดที่ตรงกับข้อมูลที่เคยทำรายการจริงก็คือแถวที่ 3 และ แถวที่ 6 ที่ลูกศรสีแดงชี้


เราจะทำการ GROUP รหัสสินค้า เพื่อลดจำนวนเรคอร์ดที่ซ้ำซ้อนออกไป


SELECT
product.*
, tb_category.name AS cate_name
, tb_category.start_used_date
FROM
product
LEFT JOIN
tb_category ON product.category_code = tb_category.code
WHERE
product.name LIKE '%กรรไกร%'
GROUP BY
product.id

ผลปรากฏว่า ชื่อหมวดหมู่ที่แสดงไม่ถูกต้อง เมื่อสังเกตวันที่ start_used_date จะเห็นว่า ได้ดึงแค่เรคอร์ดแรกมาเท่านั้น ไม่ได้ดึงตามช่วงเวลาของข้อมูลจริง


วิธีแก้ไขก็คือ เพิ่มเงื่อนไขเข้าไปในการ JOIN 

โดยกำหนด Sub Query ให้เลือกเฉพาะรายการที่เปิดใช้งานก่อนนำไปใช้งานในครั้งนั้นๆ



SELECT
product.*
, cate.name AS cate_name
, cate.start_used_date
FROM
product
LEFT JOIN
tb_category cate ON cate.id = (
SELECT MAX(bb.id)
         FROM tb_category AS bb
         WHERE bb.code = product.category_code
         AND bb.start_used_date <= product.active_date
       )
WHERE
product.name LIKE '%กรรไกร%'
GROUP BY product.id


เมื่อเราเพิ่มเงื่อนไข start_used_date <= product.active_date เข้าไปใน Sub Query ก็จะได้ ID ของเรคอร์ดที่เปิดใช้งานในช่วง active_date ของรายการนั้นๆ ซึ่งตรงกับที่เกิดขึ้นจริง


หากเห็นว่าเป็นประโยชน์ หรือมีกรณีใกล้เคียงกัน ก็ลองเอาไปปรับใช้กันดูนะครับ


สำหรับแหล่งอ้างอิง  Retrieving the last record in each group  


PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/

CodeIgniter ล็อกอินไปสักพัก Session ก็หมดอายุ

โดย SONGCHAI SAETERN

 ทำไม SESSION ใน CodeIgniter ถึงหมดอายุเร็วมากๆ!??



งง??? อยู่นานมากๆ จำได้ว่าเคยเจอปัญหานี้เมื่อครั้งที่เริ่มใช้ CodeIgnier ใหม่ๆ (จนกระทั่งเดี๋ยวนี้ก็ยังเป็นอยู่นะ ^^;) เมื่อครั้งที่ใช้ Version 2 แค่เปลี่ยนจาก Files ไปเป็น Database ก็หาย

แต่ตอนนี้เกิดขึ้นอีกแล้วววววว >O<


สรุปแล้วก็คือเกิดจากชื่อของ SESSION มันไปตรงกันกับ project อื่นๆ แล้วในฐานข้อมูล SESSION ดันใช้ Database ตัวเดียวกัน ci_sessions

เพราะมีเหตุจำเป็นต้องใช้ฐานข้อมูลเดียวกัน

PROJECT 1 ใช้ db_project.ci_sessions
PROJECT 2 ใช้ db_project.ci_sessions เช่นกัน!!


ดังนั้นเวลาเปิดหน้าเว็บ Project 2 ก็จะเกิดการสร้าง session id ใหม่ที่ตาราง ci_sessions เดียวกัน ดังนั้นใน Project 1 ก็จะโดนตัดไปโดยปริยาย


วิธีแก้ไขปัญหานี้ก็คือ

1. หลีกเลี่ยงการใช้ Database และ Table ตัวเดียวกันในการเก็บ SESSION
2. เปลี่ยน sess_cookie_name ให้เป็นชื่อที่ไม่ซ้ำกันใน config/config.php ของแต่ละ Project


ขอให้สนุกกับการเขียนโปรแกรม PHP ด้วย CodeIgniter Framework นะครับ



PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/

ตัวแสดงจำนวนสินค้าในตะกร้าสินค้า (Shopping cart Counter)

โดย SONGCHAI SAETERN

Cart counter

การใช้งานไอคอนแสดงตัวนับจำนวนสินค้าในตะกร้าสินค้านี้ จะต้องใช้ Bootstrap Framework เข้ามาช่วยในการจัดรูปแบบด้วย


Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.


ในส่วนของไอคอนแสดงตัวเลขนั้นจะมีโค้ดอยู่ 2 ส่วนด้วยกัน 


ส่วนที่ 1 CSS

<style>
.cart-box{
position: fixed;
bottom: 40px;
left: 30px;
width: 48px;
height: 48px;
z-index: 2147483000;
cursor: pointer;
background-position: 50%;
}
.btn-circle {
width: 30px;
height: 30px;
text-align: center;
padding: 6px 0;
font-size: 12px;
line-height: 1.428571429;
border-radius: 15px;
}
.btn-circle.btn-lg {
width: 50px;
height: 50px;
padding: 10px 16px;
font-size: 18px;
line-height: 1.33;
border-radius: 25px;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
font-size: 24px;
line-height: 1.33;
border-radius: 35px;
}
.cart-items-count{
position:relative;
display:flex;
text-align:center;
justify-content: center;
top:-55px;
}
.notification-counter { 
position: absolute;
left: 8px;
background-color: rgba(212, 19, 13, 1);
color: #fff;
border-radius: 3px;
padding: 1px 3px;
font: 8px Verdana;
}
</style>

ส่วนที่ 2 HTML

<div class="cart-box" id="Normal">
<ul class="nav navbar-nav">
<li class="dropdown">
  <button href="#" class="draggable dropdown-toggle btn btn-primary btn-circle btn-xl" data-toggle="dropdown" role="button" aria-expanded="false"> <span class="glyphicon glyphicon-shopping-cart"></span></button>
   <span  class="cart-items-count"><span class=" notification-counter">243</span></span>
</li>
  </ul>
</div>

เมื่อเปิดหน้าเว็บขึ้นมาดูผลลัพธ์จะได้ดังนี้




หากต้องการเปลี่ยนตำแหน่ง ให้กำหนดค่าที่ CSS .cart_box{  ใหม่



ตัวนับจำนวนจะย้ายไปยังตำแหน่งใหม่ที่ต้องการ





โค้ดฉบับเต็มสำหรับทดลอง


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>Shopping Cart COUNTER</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/3.3/examples/non-responsive/non-responsive.css" rel="stylesheet">
<style>
.cart-box{
position: fixed;
  bottom: 40px;
  left: 30px;
  width: 48px;
  height: 48px;
  z-index: 2147483000;
  cursor: pointer;
  background-position: 50%;
}
.btn-circle {
  width: 30px;
  height: 30px;
  text-align: center;
  padding: 6px 0;
  font-size: 12px;
  line-height: 1.428571429;
  border-radius: 15px;
}
.btn-circle.btn-lg {
  width: 50px;
  height: 50px;
  padding: 10px 16px;
  font-size: 18px;
  line-height: 1.33;
  border-radius: 25px;
}
.btn-circle.btn-xl {
  width: 70px;
  height: 70px;
  padding: 10px 16px;
  font-size: 24px;
  line-height: 1.33;
  border-radius: 35px;
}
.cart-items-count{
position:relative;
display:flex;
text-align:center;
justify-content: center;
top:-55px;
}
.notification-counter { 
position: absolute;
left: 8px;
background-color: rgba(212, 19, 13, 1);
color: #fff;
border-radius: 3px;
padding: 1px 3px;
font: 8px Verdana;
}
</style>
    <!-- Custom styles for this template -->
 
</head>
<body>
    <!-- Fixed navbar -->
    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <!-- The mobile navbar-toggle button can be safely removed since you do not need it in a non-responsive implementation -->
          <a class="navbar-brand" href="#">Shopping Cart</a>
        </div>
        <!-- Note that the .navbar-collapse and .collapse classes have been removed from the #navbar -->
        <div id="navbar">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">Profile</a></li>
            <li><a href="#contact">Order</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown"
  role="button" aria-haspopup="true" aria-expanded="false">Sale <span class="caret"></span></a>
            </li>
          </ul>
          <form class="navbar-form navbar-left">
            <div class="form-group">
              <input type="text" class="form-control" placeholder="Search">
            </div>
            <button type="submit" class="btn btn-default">Search</button>
          </form>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="#"></a></li>
            <li><a href="#"></a></li>
            <li><a href="#"></a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>
 
    <div class="container">
<h1>แสดงตัวนับจำนวนรายการในตะกร้าสินค้า</h1>
<div class="cart-box" id="Normal">
<ul class="nav navbar-nav">
<li class="dropdown">
  <button href="#" class="draggable dropdown-toggle btn btn-primary btn-circle btn-xl" data-toggle="dropdown" role="button" aria-expanded="false"> <span class="glyphicon glyphicon-shopping-cart"></span></button>
   <span  class="cart-items-count"><span class=" notification-counter">243</span></span>
</li>
  </ul>
</div>
    </div> <!-- /container -->
</body>
</html>


ที่มา : https://bootsnipp.com/snippets/ae8qE

PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/

วิธีสร้าง Dynamically Controllers ใน CodeIgniter Framework

โดย SONGCHAI SAETERN

How to create dynamically controllers function name in codeigniter.

1. แก้ไขไฟล์คอนฟิก application/config/routes.php

$route['product/(:any)'] = 'product/show/$1';

2. สร้างคอนโทรลเลอร์ที่ชื่อ product เอาไว้รับผ่านฟังก์ชั่น show

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class Product extends MY_Controller {

/**
* Index Page for this controller.
*/
public function show($category='')
{
    echo $category;//อยากได้ข้อมูลอะไรก็เอาค่าที่ส่งมาไปค้นหา
}

}
3. ตัวอย่างเช่น
     http://localhost/my_project/index.php/product/new  => สินค้าใหม่
     http://localhost/my_project/index.php/product/hdd  => ฮาร์ดดิสก์
     http://localhost/my_project/index.php/product/ram  => แรม

สังเกตว่าเวลาลิงค์ไปหน้า Product ไม่จำเป็นต้องสร้างฟังก์ชั่น new(), hdd(), ram() แต่เราจะวิ่งไปที่ฟังก์ชั่น show() ตามที่กำหนดไว้ในไฟล์ routes.php


ที่มา : https://expressionengine.com/forums/archive/topic/227841/building-pages-dynamically-from-a-database

แก้ปัญหา base_url() ใน CodeIgniter แสดงแค่โดเมน เข้าไม่ถึง path ของโปรเจ็กต์

โดย SONGCHAI SAETERN

การเขียนโปรแกรม PHP ด้วย CodeIgniter Framework

ในบางครั้งเราอาจจะเจอปัญหา base_url() ที่เรียกใช้แล้วได้แค่ http://localhost หรือ http://www.your-domain-name.com เท่านั้น เข้าไปไม่ถึงใน Directory ที่ใช้เก็บโปรเจ็กต์ของเรา

วิธีแก้ไขก็คือ เข้าไปในไฟล์ config.php แล้วเพิ่มโค้ดด้านล่างนี้เข้าไปแทน





application/config/config.php
$potocal = 'http'.((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '');
$directory = str_replace('//','/',dirname($_SERVER['PHP_SELF']).'/');
$base_url = $potocal . '://'.$_SERVER['HTTP_HOST'] . $directory;

$config['base_url'] = $base_url;

แก้ไข 
คำสั่งด้านบนนี้ เป็นการหาค่า URL ของหน้าเว็บปัจจุบัน ไม่ได้กำหนดค่า URL ของโปรเจ็กต์

เช่นเมื่ออยู่ใน Controller ชื่อว่า Shop จะได้ http://localhost/soap_station/index.php/shop

ดังนั้นเปลี่ยนเป็น


$potocal = 'http'.((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '');
$directory = '/your_project_directory';
$base_url = $potocal . '://'.$_SERVER['HTTP_HOST'] . $directory;

$config['base_url'] = $base_url;




PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/

แก้ปัญหาการเปิด Firewall ทำให้ Client เชื่อมต่อ MySQL ไม่ได้

โดย CyberMAN

"Allow programs to communicate through Windows Firewall"

เดิมทีปิด FireWall เอาไว้ โปรแกรมสามารถเชื่อมต่อฐานข้อมูลได้ปกติ


หลังจากเปิด Windows Firewall โปรแกรมไม่สามารถเชื่อมต่อฐานข้อมูล MySQL จากเครื่อง Client ได้


แต่เชื่อมผ่าน Localhost ได้


ให้เข้าไปเปิดที่ Control panel > Windows Firewall > Allow a program or feature through Windows Firewall เพื่อกำหนดให้ MySQL ที่เราติดตั้งไว้สามารถทำงานผ่าน Firewall ได้


หลังจากกด OK ลองกลับไปหน้าล็อกอินผ่านโปรแกรมจัดการฐานข้อมูลที่เครื่อง Client อีกครั้ง


ท่านใดใช้เครื่องมือจัดการฐานข้อมูลที่เครื่อง Client เชื่อมต่อไปยัง Server แล้วเชื่อมต่อไม่ได้ ลองตรวจสอบการตั้งค่า Windows Firewall ดูนะครับ









ข้อสังเกต



ไม่ได้เกิดจากการตั้ง Host name ของ MySQL User เพราะตั้งเป็น % เรียบร้อย (คือเชื่อมต่อได้จากทุก iP) แต่ถ้าตั้งค่า Host name แล้วแต่ยังไม่ได้จะมีข้อความแจ้งเตือนอีกแบบ

Access denied for '.....'@ 'เลขไอพีหรือComputername'

ซึ่งถ้าสั่งคิวรี่คำสั่ง

flush privileges;
 

ใน phpMyAdmin (ซึ่งล็อกอินด้วย user เดียวกันนี้ได้ปกติ) ก็จะทำการเชื่อมต่อจากไอพีด้านนอกได้

https://stackoverflow.com/questions/11760177/access-denied-for-root-user-in-mysql-command-line


บทความที่เกี่ยวข้อง

แก้ปัญหา Xampp เข้าผ่าน IP จากเครื่องอื่นไม่ได้ โดยการเปิด Firewall เชื่อมต่อ httpd ได้
https://phpcodemania.blogspot.com/2021/04/xampp-ip-firewall-httpd.html




PHP CI MANIA PHP Code Generator 
โปรแกรมช่วยสร้างโค้ด ลดเวลาการเขียนโปรแกรม เขียนโปรแกรมง่ายและสะดวกขึ้น
สนใจสั่งซื้อราคาสุดคุ้ม >> http://fastcoding.phpcodemania.com/