7 Eylül 2021 Salı

JSON Web Token (JWT) Client İçinde Nerede Saklanır

Giriş
Açıklaması şöyle.
We have three options available for storing the data on the client side and each of those has its own advantages and disadvantages. And the options are:
1. Cookie
2. Local Storage
3. Session Storage
Cookie İçinde Saklamak
Bunun için bir ön koşul var. Açıklaması şöyle. Yani JWT 4K'dan küçük olmalı.
The purpose of JWTs is to be stateless, right? Cookies are capped out at 4k, which means the JWT needs to be < 4k for this to work.
Cookie içinde SameSite=strict, HttpOnly gibi bayraklarla birlikte saklamak.
- SameSite=strict CSRF saldırısına karşı korur.
- HttpOnly ise XSS saldırısına karşı korur. HttpOnly tarayıcıya enjekte edilen javascript kodlarının token'a erişip başka yere göndermesini engeller
Açıklaması şöyle
... using cookies alone is not the solution but extra steps to prevent XSS attack must be taken by enabling “HTTP-only” parameter in cookies which basically do not allow any third party JavaScript code to read your cookies and enabling the secure flag which transports your cookies only through HTTPS.
Local Storage İçinde Saklamak
Bu önerilmiyor. Açıklaması şöyle.
Local storage wasn’t designed to be used as a secure storage mechanism in a browser. It was designed to be a simple string only key/value store that developers could use to build slightly more complex single-page apps.
— Randall Degges

When you store sensitive information in local storage, you’re essentially using the most dangerous thing in the world(javascript) to store your most sensitive information in the worst vault ever created.
— Randall Degges
Session Storage İçinde Saklamak
Açıklaması şöyle.
The downside is that you need to manage a cache on the API side, but this is easily doable.

If you’re using JWTs anyway, you STILL NEED to have centralized sessions that handles revocation, right?.

gRPC Error Handling

Giriş
Açıklaması şöyle
By default, gRPC relies heavily on status code for error handling. 
Sunucu tarafından fırlatılan exception gRPC tarafından StatusRuntimeException'a çevrilir.

Örnek
Şöyle yaparız. Burada ServiceException kendi sınıfımız
import io.grpc.StatusRuntimeException;

//Client call
public Product getProduct(String productId) {
  Product product = null;
  try {
    var request = GetProductRequest.newBuilder().setProductId(productId).build();
    var productApiServiceBlockingStub = ProductServiceGrpc.newBlockingStub(managedChannel);
    var response = productApiServiceBlockingStub.getProduct(request);
    // Map to domain object
    product = ProductMapper.MAPPER.map(response);
  } catch (StatusRuntimeException error) {
    log.error("Error while calling product service, cause {}", error.getMessage());
    throw new ServiceException(error.getCause());
  }
  return product;
}
Ancak bir problem var. O da hata mesajının kaybolması. Çıktı olarak şunu alırız
io.grpc.StatusRuntimeException: UNKNOWN
Açıklaması şöyle
gRPC wraps our custom exception in StatusRuntimeException and swallows the error message and assigns a default status code UNKNOWN.
Bunu düzeltmek için sunucu tarafında şöyle yaparız. Bu sefer onError() metodunu çağırıyoruz.
//Server Product Service API
public void getProduct(
    GetProductRequest request, StreamObserver<GetProductResponse> responseObserver) {
  try {
    ...
    responseObserver.onNext(response);
    responseObserver.onCompleted();
  } catch (ResourceNotFoundException error) {
    var status = Status.NOT_FOUND.withDescription(error.getMessage()).withCause(error);
    responseObserver.onError(status.asException());
  }
}
İstemci tarafında çıktı olarak şunu alırız. 
Error while calling product service, cause NOT_FOUND: Product ID not found
Hata mesajı düzgün ancak hala exception içindeki getCause() null döner. Sebebinin açıklaması şöyle. Yani io.grpc.Status.withCause() çağrısı orijinal exception'ı göndermiyor.
Create a derived instance of Status with the given cause. However, the cause is not transmitted from server to client.
Şöyle yaparız. Bu sefer io.grpc.Metadata kullanılıyor
public void getProduct(
    GetProductRequest request, StreamObserver<GetProductResponse> responseObserver) {
  try {
    ...
    responseObserver.onNext(response);
    responseObserver.onCompleted();
  } catch (ResourceNotFoundException error) {
    Map<String, String> errorMetaData = error.getErrorMetaData();
    var metadata = new Metadata();    
    errorMetaData.entrySet().stream() 
        .forEach(
            entry ->
                metadata.put(
                    Metadata.Key.of(entry.getKey(), Metadata.ASCII_STRING_MARSHALLER),
                    entry.getValue()));
    var statusRuntimeException =
        Status.NOT_FOUND.withDescription(error.getMessage()).asRuntimeException(metadata); 
    responseObserver.onError(statusRuntimeException);
  }
}
İstemci tarafında şöyle yaparız
} catch (StatusRuntimeException error) {

  Metadata trailers = error.getTrailers();
  Set<String> keys = trailers.keys();

  for (String key : keys) {
    Metadata.Key<String> k = Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER);
    log.info("Received key {}, with value {}", k, trailers.get(k));
  }
}
İstemci tarafında çıktı olarak şunu alırız. 
Received key Key{name='resource_id'}, with value 32c29935-da42-4801-825a-ac410584c281 
Received key Key{name='content-type'}, with value application/grpc 
Received key Key{name='message'}, with value Product ID not found
- Bütün bunlarlar uğraşmak yerine Google Richer Error Model kullanılabilir.
- Ayrıca sunucu tarafında bir sürü exceptıon yakalayıp io.grpc.Status dönmek yerine io.grpc.ServerInterceptor arayüzünden kalıtıp ortak bir kod yazılabilir.
- Eğer Spring kullanıyorsak aynı şeyi şöyle yaparız
@GrpcAdvice
public class ExceptionHandler {

  @GrpcExceptionHandler(ResourceNotFoundException.class)
  public StatusRuntimeException handleResourceNotFoundException(ResourceNotFoundException
cause) {
    var errorMetaData = cause.getErrorMetaData();
    var errorInfo =
        ErrorInfo.newBuilder()
            .setReason("Resource not found")
            .setDomain("Product")
            .putAllMetadata(errorMetaData)
            .build();
    var status =
        com.google.rpc.Status.newBuilder()
            .setCode(Code.NOT_FOUND.getNumber())
            .setMessage("Resource not found")
            .addDetails(Any.pack(errorInfo))
            .build();
    return StatusProto.toStatusRuntimeException(status);
  }
}


gRPC Protobuf Dosyası

Giriş
Protokolü anlamak için Protobuf yazısına bakabilirsiniz.

- Dosya uzantısı *.proto şeklindedir.
- İlk satır syntax'i belirtir
- İkinci satır package ile paket ismini belirtir
- Daha sonra option satırları ile seçenekler belirtilir
- Daha sonra message yapıları gelir. Mesaj yapıları iç içe (nested) olabilir veya birbirlerine kullanabilirler. Field olarak string, int64, int32, double, repeated kelimeleri kullanılabilir.
- Daha sonra service metodları gelir. gRPC Protobuf Dosyası - Service Tanımlama yazısına bakabilirsiniz.

1. Option Seçenekleri
Çok kullanılan option değerleri şöyle. Açıklaması şöyle
java_multiple_files — option allows the compiler to create different classes for all it’s components i.e. HelloRequest.java and HelloReply.java
java_package — option allows the compiler to create a new package and keep the gRPC classes in it
Örnek
Şöyle yaparız
syntax = "proto3"; import "google/protobuf/timestamp.proto"; package mobilepackage; option java_package = "com.ardab.gprc.mobilepackage"; option java_multiple_files = true;
2. Scalar Value Types
Boolean için şu kullanılır 
bool
String için şu kullanılır 
string
byte[] için şu kullanılır 
bytes
Floating point için şunlar kullanılır 
double, float 
Variable length encoding için şunlar kullanılır . sint32 negatif sayılarda int32'den daha iyidir.
int32, int64,
uint32, uint64,
sint32, sint64
Fixed length encoding için şunlar kullanılır. fixed32 C++'ta uint32, sfixed32 C++'ta int32 anlamına gelir. Yani signed/unsigned farkı yaratır. Java'da her ikisi de int anlamına gelir
fixed32, fixed64
sfixed32, sfixed64

Örnek - Tek Mesaj ve Sadece Scalar Tipler (string vs)
Şöyle yaparız
syntax = "proto3";
package com.codingharbour.protobuf;

message SimpleMessage {
  string content = 1;
  string date_time = 2;
}
Açıklaması şöyle
In the first line, we define that we're using Protobuf version 3. Our message type called SimpleMessage defines two string fields: content and date_time. Each field is assigned a so-called field number, which has to be unique in a message type. These numbers identify the fields when the message is serialized to the Protobuf binary format. Google suggests using numbers 1 through 15 for most frequently used fields because it takes one byte to encode them.

Protobuf supports common scalar types like string, int32, int64 (long), double, bool etc. For the full list of all scalar types in Protobuf check the Protobuf documentation.
Örnek - Farklı Bir Mesaj İçeren Complex Type
Şöyle yaparız
message Order {
  int64 order_id = 1;
  int64 date_time = 2;
  repeated Product product = 3;
}

message Product {
  int32 product_id = 1;
  string name = 2;
  string description = 3;
}
3. Google Tipleri
google.protobuf.Timestamp
Örnek
Şöyle yaparız
syntax = "proto3"; import "google/protobuf/timestamp.proto"; ... message SubscriptionExceptionResponse { google.protobuf.Timestamp timestamp = 1; SubscriptionErrorCode error_code = 2; } ...
4. Kullanım Örnekleri

Örnek - java_multiple_files option
Şöyle yaparız
syntax = "proto3";
option java_multiple_files = true;
package com.milind.grpc;

message Order {
  string currency = 1;
  double totalAmount = 2;
  string orderId = 3;
  string emailAddress = 4;
  string cartURL =5;
  repeated LineItems lineItems = 6;
}

message LineItems {
  string sku = 1;
  string name = 2;
  string description = 3;
  string category = 4;
  string other = 5;
  double unitPrice = 6;
  double salePrice = 7;
  double quantity = 8;
  double totalPrice = 9;
}

message OrderConfirmation {
  string orderId = 1;
  repeated ConfirmedLineItems confirmedLineItems = 2;
}

message ConfirmedLineItems {
  string sku = 1;
  double confirmQuantity = 2;
}
Örnek - nested message
Şöyle yaparız
syntax = "proto3";
package demo.camel;
option java_package = "demo.camel";
option java_outer_classname = "TransactionProtos";

message Transaction {
  string transactionid = 1;
  string transactiontype = 2;
  User sender = 3;
  string currency = 4;
  double amt = 5;
  string receiverid = 6;
 
  message User {
    string username = 1;
    string userid = 2;
  }
}
5. FieldMask Tipi
Açıklaması şöyle
FieldMask is a protobuf message. There are a number of utilities and conventions on how to use this message when it is present in an RPC request. A FieldMask message contains a single field named paths, which is used to specify fields that should be returned by a read operation or modified by an update operation.
Mesaj şöyle
message FieldMask {
  // The set of field mask paths.
  repeated string paths = 1;
}
FieldMask mesajı alan ismini string olarak taşıyor. Dolayısıyla sunucu tarafında alan isimlerinde değişiklik yapılırsa sorun çıkabilir. Şeklen şöyle


Örnek
Elimizde şöyle bir servis olsun
// Contains Production-related information  
message Production {
  string id = 1;
  string title = 2;
  ProductionFormat format = 3;
  repeated ProductionScript scripts = 4;
  ProductionSchedule schedule = 5;
  // ... more fields
}

service ProductionService {
  // returns Production by ID
  rpc GetProduction (GetProductionRequest) returns (GetProductionResponse);
}

import "google/protobuf/field_mask.proto"; message GetProductionRequest { string production_id = 1; google.protobuf.FieldMask field_mask = 2; }

message GetProductionResponse {
  Production production = 1;
}
İstemci çağırmak için şöyle yapar
FieldMask fieldMask = FieldMask.newBuilder()
    .addPaths("title")
    .addPaths("format")
    .build();

GetProductionRequest request = GetProductionRequest.newBuilder()
    .setProductionId(LA_CASA_DE_PAPEL_PRODUCTION_ID)
    .setFieldMask(fieldMask)
    .build();
Eğer alan isimlerini string olarak kullanmak istemiyorsak istemci çağırmak için şöyle yapar
FieldMask fieldMask = FieldMaskUtil.fromFieldNumbers(Production.class,
Production.TITLE_FIELD_NUMBER, Production.FORMAT_FIELD_NUMBER); GetProductionRequest request = GetProductionRequest.newBuilder() .setProductionId(LA_CASA_DE_PAPEL_PRODUCTION_ID) .setFieldMask(fieldMask) .build();
Sunucu tarafında sadece istenilen alanları dönmek için şöyle yaparız
@Override
public void getProduction(GetProductionRequest request, 
                          StreamObserver<GetProductionResponse> response) {
   
    Production production = fetchProduction(request.getProductionId());
    FieldMask fieldMask = request.getFieldMask();

    Production.Builder productionWithMaskedFields = Production.newBuilder();
    FieldMaskUtil.merge(fieldMask, production, productionWithMaskedFields);
   
    GetProductionResponse response = GetProductionResponse.newBuilder()
        .setProduction(productionWithMaskedFields).build();
    responseObserver.onNext(response);
    responseObserver.onCompleted();
}
Eğer çağrıyı da belli boolean logic ile yapmak istersek şöyle yaparız
private static final String FIELD_SEPARATOR_REGEX = "\\.";
private static final String MAX_FIELD_NESTING = 2; private static final String SCHEDULE_FIELD_NAME = // (1) Production.getDescriptor() .findFieldByNumber(Production.SCHEDULE_FIELD_NUMBER).getName(); @Override public void getProduction(GetProductionRequest request, StreamObserver<GetProductionResponse> response) { FieldMask canonicalFieldMask = FieldMaskUtil.normalize(request.getFieldMask()); // (2) boolean scheduleFieldRequested = // (3) canonicalFieldMask.getPathsList().stream() .map(path -> path.split(FIELD_SEPARATOR_REGEX, MAX_FIELD_NESTING)[0]) .anyMatch(SCHEDULE_FIELD_NAME::equals); if (scheduleFieldRequested) { ProductionSchedule schedule = makeExpensiveCallToScheduleService(request.getProductionId()); // (4) ... } ... }
Açıklaması şöyle
(1) The SCHEDULE_FIELD_NAME constant contains the name of the field. This code sample uses message type Descriptor and FieldDescriptor to lookup field name by field number. The difference between protobuf field names and field numbers is described in the Protobuf Field Names vs Field Numbers section above.
(2) FieldMaskUtil.normalize() returns FieldMask with alphabetically sorted and deduplicated field paths (aka canonical form).
(3) Expression (lines ##14 - 17) that yields the scheduleFieldRequestedvalue takes a stream of FieldMask paths, maps it to a stream of top-level fields, and returns true if top-level fields contain the value of the SCHEDULE_FIELD_NAME constant.
(4) ProductionSchedule is retrieved only if scheduleFieldRequested is true.
6. Modifiers
deprecated
Örnek
Şöyle yaparız
message Production {
  string id = 1;
  string title = 2 [deprecated = true];  // use "title_name" field instead
  ProductionFormat format = 3;
  repeated ProductionScript scripts = 4;
  ProductionSchedule schedule = 5;
  string title_name = 6;
}

VGA (Video Graphics Array) Ekran Kartı

VGA Ne Demek?
Açıklaması şöyle
"VGA" has two meanings:

- A specific graphics card, the IBM Video Graphics Array from 1987, which supported resolutions up to 640×480 (now known as the "VGA" resolution), and which also introduced a new video connector type that's now known as the "VGA" connector.

- The 15-pin analog video connector introduced by the IBM VGA but later used for 35 years by probably every other graphics card in the world.

So when people talk about the 640×480 limit, they refer to what the IBM "VGA" chip could output. The physical connection, however, can handle much higher resolutions, although it depends on the cable quality and shielding (at some point the analog signal begins to deteriorate).
VGA Ekran Kartı artık yok ancak VGA connector halen kullanılıyor

VGA connector
Şeklen şöyle

Açıklaması şöyle. Yani bu kabloyla her piksel teker teker gönderiliyor.
VGA is an older signalling standard where a pixel is transmitted on only three pins by using analog voltages.

Each of these voltages is represented as a voltage level between 0 volts (no intensity) and 0.7 volts (full intensity). So a pure black pixel will have the signal level of 0v,0v,0v on the three lines, while a pure white pixel will have an ideal signal level of 0.7v,0.7v,0.7v on the three lines and so on.


This means that VGA is capable of transmitting in any resolution at all, one pixel at a time. Cable quality and transmission speed count strongly for the quality and responsiveness of the display.

Enhanced Graphics Adapter - EGA
Açıklaması şöyle. VGA'nın atası EGA idi.
The 16-color modes on the VGA use a hardware design borrowed from the EGA.
Ekran Çözünürlüğü
Açıklaması şöyle. Çözünürlük ilk başta sadece 640x480 ve 16 renk idi.
Original VGA supported 640x480 16-colors and 720x400 (essentially a small step up from the 720x350 monochrome (MDA) text, but with color). While VGA has come to mean "any video card and monitor that uses a blue 15-pin connector" that's not what it originally meant. Even when VGA (e.g., SVGA) started to support higher resolution, that was not, initially, directly supported by BIOS, DOS, etc. but rather extra modes with special drivers in Windows, some games, etc. I would not expect a vintage card to produce 800x600 (or higher) without loading some extra software first, and it might not do it at all.
Color Palette
Resmin renk uzayı ne olursa olsun, eskiden VGA ekran kartlarında "color palette" kullanılırdı.  Bu renklerin neye göre seçildiğini açıklaması şöyle
some colors are for compatibility with older gfx like EGA,CGA and the rest is a compromise between usually needed colors for Apps at that time
Daha detaylı bir açıklama şöyle
The VGA default palette in the 256 colour mode (Mode 13h) first has 16 color entries from CGA (which is also same as default 16-color EGA palette and the only palette for 320x200 EGA mode)

Next 16 color entries are 16 shades of gray.

And the next 216 color entries has been already mentioned; they are sets of 24 hues, in 3 different saturation values, and in 3 different brightness values. 24 × 3 × 3 = 216.

The final 8 colour entries are black, or maybe left undefined so BIOS does not overwrite them when changing modes.
Koddan Color Palette'e Dönüşüm
Açıklama şöyle.  Yani eskiden VGA kartları renk için toplam 24 bit kullanmasına rağmen bir seferde sadece 265 renk gösterebilirdi. Çünkü video bellek alanı azdı. Günümüzde zaten Color Depth olarak 32 bit (true color) kullanılıyor.
How Windows Uses Color
One of the benefits of a device-independent output model is that you can specify the colors an application uses without regard for the physical characteristics of the output device. When you pass a color to the Windows GDI, you pass a COLORREF value containing 8 bits each for red, green, and blue. The RGB macro combines individual red, green, and blue values into a single COLORREF. The statement

COLORREF clr = RGB (255, 0, 255); 
creates a COLORREF value named clr that represents magenta—the color you get when you mix equal parts red and blue. Conversely, you can extract 8-bit red, green, and blue values from a COLORREF value with the GetRValue, GetGValue, and GetBValue macros. A number of GDI functions, including those that create pens and brushes, accept COLORREF values.

What the GDI does with the COLORREF values you pass it depends on several factors, including the color resolution of the video hardware and the context in which the colors are used. In the simplest and most desirable scenario, the video adapter is a 24-bits-per-pixel device and COLORREF values translate directly into colors on the screen. Video adapters that support 24-bit color, or true color, are becoming increasingly common, but Windows still runs on millions of PCs whose video adapters are limited to 4 or 8 bits per pixel. Typically, these devices are palletized devices, meaning that they support a wide range of colors but can display only a limited number of colors at one time. A standard VGA, for example, can display 262,144 different colors—6 bits each for red, green, and blue. However, a VGA running at a resolution of 640 by 480 pixels can display only 16 different colors at once because each pixel is limited to 4 bits of color information in the video buffer. The more common case is a video adapter that can display more than 16.7 million colors but can display only 256 colors at once. The 256 colors that can be displayed are determined from RGB values that are programmed into the adapter's hardware palette.
Fade İşlemi
Fade Effect/ Gamma Correction veya OpeGL'deki GL_FUNC_REVERSE_SUBTRACT için yapılan işlemin açıklaması şöyle
For PC VGA, which definitely has just 256 colours on screen that are picked from the 16 million available via a look up table, such fades are definitely done by changing just the palette definition every 0.1 seconds or something like that.
Eğer fade işlemini kodla yapmak istersek açıklaması şöyle. Java'daki Color sınıfının darker() metoduna bakılabilir.
If you do it naively, you just subtract a fixed value from each R, G, B component on each frame. The consequence is that brighter colours take longer to reach black (zero) since they start from higher RGB values. That's what seems to be happening in your screenshots

5 Eylül 2021 Pazar

Istio

Giriş
Açıklaması şöyle
Istio: A full open-source solution founded by IBM, Google and Lyft.
API Gateway
Istio, VirtualService ve Gateway sunar

Sidecar
Her servisin yanında çalışır. Böylece servisler arasındaki trafik izlenebilir ve yönetilebilir.

CircuitBreaker
Örnek
Şöyle yaparız
  
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: circuit-breaker-for-the-whole-default-namespace
spec:
# This is the name of the k8s service that we're configuring
  host: "demo -service.default.svc.cluster.local" 

  trafficPolicy:
    outlierDetection: # Circuit Breakers have been SWITCHED ON
      maxEjectionPercent: 100
      consecutive5xxErrors: 2
      interval: 10s
      baseEjectionTime: 30s
Açıklaması şöyle
- consecutive5xxErrors Number of 5xx errors before a host is ejected from the connection pool.
- maxEjectionPercent Maximum % of hosts in the load balancing pool for the upstream service that can be ejected. Defaults to 10%.
- baseEjectionTime Minimum ejection duration. A host will remain ejected for a period equal to the product of minimum ejection duration and the number of times the host has been ejected.
- interval Time interval between ejection sweep analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.

3 Eylül 2021 Cuma

White Box veya Structure Based Testing

Giriş
ISTQB dokümanında White Box ile Structure-based test tekniği eş anlamlı olarak kullanılıyor. 

Bu test tekniği "based on software or system structure" olarak niteleniyor. Yani test koda göre tasarlanıyor. Test teknikleri şöyle

Code Coverage : Kodun %80 kapsanması
Decision veya Branch Coverage : if/else'lerin kapsanması
Path Coverage : Birbirinden bağımsız akışın kapsanması
Statement Coverage : Her statement'ın kapsanması

Decision veya Branch Coverage
Decision coverage yaparken düşülen en büyük yanılgı if/else koşulunda else kısmı yoksa test yazılmayacağı düşüncesi. Ancak true ve false seçenekleri için test yazılması gerekir. Bu testing açıklaması şöyle
Instead of only exercising branches with instructions, we will take each of the "true" and "false" alternatives (branches) of each decision, whether or not there are instructions in these branches.
Bu testin kullanım yerinin açıklaması şöyle
Decision coverage is required in the aerospace industry for all software where a failure would have dangerous consequences (Category B software)
MC/DC Coverage
MC/DC Coverage havacılık dünyasında Level A yazılımda kullanılır.

Statement Coverage
Statement Coverage havacılık dünyasında Level C yazılımda kullanılır.


2 Eylül 2021 Perşembe

SQL Subselect - Aynı zamanda Subquery de deniliyor

Giriş
Açıklaması şöyle. İç içe sorgularda içteki sorgu önce çalıştırılır. 
A subquery is an SQL query that is nested in another SQL query. They assist queries in creating conditions for a WHERE clause to filter rows and perform operations on them. Subqueries can be used with SELECT, INSERT, UPDATE, and DELETE statements.
Açıklaması şöyle. Yani Subselect aslında Join'e bir alternatiftir.
Advantages of Subqueries
- Subqueries improve query readability as opposed to joins by structuring them into isolated parts.
- It is easy to understand and maintain subqueries easily.
- Subqueries can replace complex joins and unions.

Disadvantages of Subqueries
- Subqueries cannot modify a table and select from the same table in the same SQL statement.
- Subqueries are an expensive task, so it's faster to use a join operation.
Mesela arasında aynı tablo içinde bilinen bir satırdan farklı özelliklere sahip diğer satırları bulmak için kullanılması sayılabilir. 

Örnek - Aynı Tablo
Maaşı Tom'dan büyük olanları bulmak için şöyle yaparız
SELECT * FROM employee WHERE sal > (SELECT sal WHERE name='TOM');
Örnek - Farklı İki Tablo Select
Şöyle yaparız
SELECT * FROM buyer WHERE buyername IN (SELECT buyer FROM sku_data);
Örnek - Farklı İki Tablo Update
Şöyle yaparız
UPDATE order_item SET prices=prices*1.1 WHERE sku IN (SELECT sku FROM catalog_sku_2016);
Örnek - Farklı İki Tablo Delete
Şöyle yaparız
DELETE FROM inventory WHERE warehouseid IN (SELECT warehouseid FROM warehouse WHERE
squarefeet < 130000);
Örnek - Nested Subqueries
Şöyle yaparız
SELECT *
FROM catalog_sku_2017
WHERE sku IN
    (
        SELECT sku
        FROM inventory
        WHERE warehouseid IN
        (
            SELECT warehouseid
            FROM warehouse
            WHERE squarefeet > 130000
        )
    );