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 ได้
http://blog.phpcodemania.com/2021/04/xampp-ip-firewall-httpd.html




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