-
基本类型和包装类之间的转换基本类型和包装类之间经常需要互相转换,以 Integer 为例(其他几个包装类的操作雷同哦):在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了。那什么是装箱和拆箱呢?我们分别来看下装箱:把基本类型转换成包装类,使其具有对象的性质,又可分为手动装箱和自动装箱拆箱:和装箱相反,把包装类对象转换成基本类型的值,又可分为手动拆箱和自动拆箱在实际转换时,使用Integer类的构造方法和Integer类内部的intValue方法实现这些类型之间的相互转换,实现的代码如下: int n = 10; Integer in = new Integer(100); //将int类型转换为Integer类型 Integer in1 = new Integer(n); //将Integer类型的对象转换为int类型 int m = in.intValue();JDK5.0的一个新特性是自动装箱和自动拆箱。 自动装箱 - 基本类型就自动地封装到与它相似类型的包装中,如:Integer i = 100; - 本质上是,编译器编译时自动添加:Integer i = new Integer(100); 自动拆箱 - 包装类对象自动转换成基本数据类型。如:int a = new Integer(100); - 本质是上,编译器编译时自动添加:int a = new Integer(100).intValue();扩展文章:JAVA菜鸟入门篇 - Java基本数据类型常用包装类(二十六)基本类型和字符串之间的转换在程序开发中,我们经常需要在基本数据类型和字符串之间进行转换。其中,基本类型转换为字符串有三种方法:1. 使用包装类的 toString() 方法2. 使用String类的 valueOf() 方法3. 用一个空字符串加上基本类型,得到的就是基本类型数据对应的字符串再来看,将字符串转换成基本类型有两种方法:1. 调用包装类的 parseXxx 静态方法2. 调用包装类的 valueOf() 方法转换为基本类型的包装类,会自动拆箱PS:其他基本类型与字符串的相互转化这里不再一一列出,方法都类似原文链接:https://blog.csdn.net/weixin_34197488/article/details/86393556
-
button11.setOnClickListener(v -> { try { ObsClient obsClient = new ObsClient(ak, sk,endPoint); // 流式下载 ObsObject obsObject = obsClient.getObject("namexiao", "Speedtestfile.mp4"); // 读取对象内容 InputStream input = obsObject.getObjectContent(); FileOutputStream fileOutputStream = new FileOutputStream("/Download/myfile.mp4"); byte[] b = new byte[1024]; //ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len; while ((len = input.read(b)) != -1) { fileOutputStream.write(b, 0, len); }// System.out.println("getObjectContent successfully");// System.out.println(new String(bos.toByteArray())); fileOutputStream.close(); input.close(); } catch (ObsException e) { System.out.println("getObjectContent failed"); // 请求失败,打印http状态码 System.out.println("HTTP Code:" + e.getResponseCode()); // 请求失败,打印服务端错误码 System.out.println("Error Code:" + e.getErrorCode()); // 请求失败,打印详细错误信息 System.out.println("Error Message:" + e.getErrorMessage()); // 请求失败,打印请求id System.out.println("Request ID:" + e.getErrorRequestId()); System.out.println("Host ID:" + e.getErrorHostId()); e.printStackTrace(); } catch (Exception e) { System.out.println("getObjectContent failed"); // 其他异常信息打印 e.printStackTrace(); } });点击按键触发事件是程序闪退,求助大佬如何解决
-
【问题来源】黑龙江农信社【问题简要】开发 vxml 版本自助语音流程时,播放音频文件时,日志中出现error.noresource 异常【问题类别】vxml 版本自主语音流程开发【AICC解决方案版本】AICC 版本:AICC 8.0.71【问题现象描述】代码中 欲播放 2003.wav 音频文件但通过流程跟踪获得 的日志里提示 error.noresource异常导致代码无法继续执行但在异常捕获代码中播放的 2003.wav 可以正常播放想问下,什么情况会造成 error.noresource 异常,谢谢
-
代码中 欲播放 2003.wav 音频文件但通过流程跟踪获得 的日志里提示 error.noresource异常导致代码无法继续执行但在异常捕获代码中播放的 2003.wav 可以正常播放想问下,什么情况会造成 error.noresource 异常
-
支持Fast DFS、服务器、OSS等上传方式介绍在实际的业务中,可以根据客户的需求设置不同的文件上传需求,支持普通服务器上传+分布式上传(Fast DFS)+云服务上传OSS(OSS)软件架构为了方便演示使用,本项目使用的是前后端不分离的架构前端:Jquery.uploadFile后端:SpringBoot前期准备:FastDFS、OSS(华为)、服务器实现逻辑通过 application 配置对上传文件进行一个自定义配置,从而部署在不同客户环境可以自定义选择方式。优点:一键切换;支持当前主流方式;缺点:迁移数据难度增加:因为表示FileID在对象存储和服务器上传都是生成的UUID,而FastDFS是返回存取ID,当需要迁移的时候,通过脚本可以快速将FastDFS的数据迁移上云,因为存储ID可以共用。但是对象存储和服务器上传的UUID无法被FastDFS使用,增加迁移成本核心代码package com.example.file.util;import com.example.file.common.ResultBean;import com.github.tobato.fastdfs.domain.StorePath;import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;import com.github.tobato.fastdfs.service.FastFileStorageClient;import com.obs.services.ObsClient;import com.obs.services.exception.ObsException;import com.obs.services.model.DeleteObjectRequest;import com.obs.services.model.GetObjectRequest;import com.obs.services.model.ObsObject;import com.obs.services.model.PutObjectResult;import io.micrometer.common.util.StringUtils;import jakarta.servlet.http.HttpServletResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.mock.web.MockMultipartFile;import org.springframework.web.multipart.MultipartFile;import java.io.*;import java.util.Objects;import java.util.UUID;@Slf4jpublic class FileUtil { /** * * @param file 文件 * @param uploadFlag 标识 * @param uploadPath 上传路径 * @param endPoint 域名 * @param ak ak * @param sk sk * @param bucketName 桶名字 * @return fileId 用于下载 */ public static ResultBean uploadFile(MultipartFile file, String uploadFlag, String uploadPath, FastFileStorageClient fastFileStorageClient, String endPoint, String ak, String sk, String bucketName) { if (StringUtils.isBlank(uploadFlag)){ ResultBean.error("uploadFlag is null"); } switch (uploadFlag){ case "fastDFS": return uploadFileByFastDFS(file,fastFileStorageClient); case "huaweiOOS": return uploadFileByHuaweiObject(file, endPoint, ak, ak, bucketName); case "server": default: return uploadFileByOrigin(file,uploadPath); }} /** * 上传文件fastDFS * @param file 文件名 * @return */ private static ResultBean uploadFileByFastDFS(MultipartFile file,FastFileStorageClient fastFileStorageClient){ Long size=file.getSize(); String fileName=file.getOriginalFilename(); String extName=fileName.substring(fileName.lastIndexOf(".")+1); InputStream inputStream=null; try { inputStream=file.getInputStream(); //1-上传的文件流 2-文件的大小 3-文件的后缀 4-可以不管他 StorePath storePath=fastFileStorageClient.uploadFile(inputStream,size,extName,null); log.info("[uploadFileByFastDFS][FullPath]"+storePath.getFullPath()); return ResultBean.success(storePath.getPath()); }catch (Exception e){ log.info("[ERROR][uploadFileByFastDFS]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 对象存储上传 * @param file 文件名 * @return */ private static ResultBean uploadFileByHuaweiObject(MultipartFile file, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); InputStream inputStream=null; try { inputStream=file.getInputStream(); // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); PutObjectResult result = obsClient.putObject(bucketName, fileName, inputStream); obsClient.close(); return ResultBean.success(fileName); }catch (ObsException e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getErrorMessage()); return ResultBean.error(e.getErrorMessage()); }catch (Exception e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 上传文件原本方法 * @param file 文件名 * @return */ private static ResultBean uploadFileByOrigin(MultipartFile file,String uploadPath){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); File targetFile = new File(uploadPath); if (!targetFile.exists()) { targetFile.mkdirs(); } FileOutputStream out = null; try { out = new FileOutputStream(uploadPath + fileName); out.write(file.getBytes()); } catch (IOException e) { log.info("[ERROR][uploadFileByOrigin]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { try { out.flush(); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } } return ResultBean.success(fileName); } /** * 下载 * @return */ public static byte[] downloadFile(String fileId,String uploadFlag,String uploadPath ,FastFileStorageClient fastFileStorageClient, String group, String endPoint,String ak,String sk, String bucketName) { byte[] result=null; switch (uploadFlag){ case "fastDFS": result =downloadFileByFastDFS(fileId,fastFileStorageClient,group); break; case "huaweiOOS": result =downloadFileByHuaweiObject(fileId, endPoint, ak, sk, bucketName); break; case "server": default: String path2 = uploadPath + fileId; path2 = path2.replace("//", "/"); result=downloadFileByOrigin(path2); break; } return result; } /** * 下载文件fastDFS * @param fileId 文件名 * @return */ private static byte[] downloadFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group){ DownloadByteArray callback=new DownloadByteArray(); byte[] group1s=null; try { group1s = fastFileStorageClient.downloadFile(group, fileId, callback); }catch (Exception e){ log.info("[ERROR][downloadFileByFastDFS]"+e.getMessage()); } return group1s; } /** * 下载文件对象存储 * @param fileId 文件名 * @return */ private static byte[] downloadFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ byte[] bytes =null; try { // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 构造GetObjectRequest请求 GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileId); // 执行下载操作 ObsObject obsObject = obsClient.getObject(getObjectRequest); bytes = inputStreamToByteArray(obsObject.getObjectContent()); // 关闭OBS客户端 obsClient.close(); return bytes; }catch (ObsException e){ log.info("[ERROR][downloadFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return bytes; } /** * * @param input * @return * @throws IOException */ private static byte[] inputStreamToByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } /** * 下载文件 * @param fileId 文件名 * @return */ private static byte[] downloadFileByOrigin(String fileId){ File file =new File(fileId); InputStream inputStream=null; byte[] buff = new byte[1024]; byte[] result=null; BufferedInputStream bis = null; ByteArrayOutputStream os = null; try { os=new ByteArrayOutputStream(); bis = new BufferedInputStream(new FileInputStream(file)); int i = bis.read(buff); while (i != -1) { os.write(buff, 0, buff.length); i = bis.read(buff); } result=os.toByteArray(); os.flush(); } catch (Exception e) { log.info("[ERROR][downloadFile]"+e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } /** * 删除文件 * @param fileId 文件ID * @return 删除失败返回-1,否则返回0 */ public static boolean deleteFile(String fileId,String fastDFSFlag,FastFileStorageClient fastFileStorageClient, String group,String uploadPath) { boolean result=false; if (StringUtils.isNotBlank(fastDFSFlag)&& fastDFSFlag.trim().equalsIgnoreCase("true")){ result =deleteFileByFastDFS(fileId,fastFileStorageClient,group); }else { String path2 = uploadPath + fileId; path2 = path2.replace("//", "/"); result=deleteByOrigin(path2); } return result; } private static boolean deleteByOrigin(String fileName) { File file = new File(fileName); // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 if (file.exists() && file.isFile()) { if (file.delete()) { return true; } else { return false; } } else { return false; } } private static boolean deleteFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group) { try { String groupFieId=group+"/"+fileId; StorePath storePath = StorePath.praseFromUrl(groupFieId); fastFileStorageClient.deleteFile(storePath.getGroup(), storePath.getPath()); } catch (Exception e) { log.info("[ERROR][deleteFileByFastDFS]"+e.getMessage()); return false; } return true; } /** * 生成fileId * @param fileName * @return */ private static String renameToUUID(String fileName) { return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1); } /** * 删除文件对象存储 * @param fileId 文件名 * @return */ private static boolean deleteFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ try { // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 构造GetObjectRequest请求 DeleteObjectRequest getObjectRequest = new DeleteObjectRequest(bucketName, fileId); // 执行删除操作 obsClient.deleteObject(getObjectRequest); // 关闭OBS客户端 obsClient.close(); }catch (ObsException e){ log.info("[ERROR][deleteFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return true; } /** * 文件数据输出(image) * @param fileId * @param bytes * @param res */ public static void fileDataOut(String fileId, byte[] bytes, HttpServletResponse res){ String[] prefixArray = fileId.split("\\."); String prefix=prefixArray[1]; File file =new File(fileId); res.reset(); res.setCharacterEncoding("utf-8");// res.setHeader("content-type", ""); res.addHeader("Content-Length", "" + bytes.length); if(prefix.equals("svg")) prefix ="svg+xml"; res.setContentType("image/"+prefix); res.setHeader("Accept-Ranges","bytes"); OutputStream os = null; try {// os = res.getOutputStream();// os.write(bytes);// os.flush(); res.getOutputStream().write(bytes); } catch (IOException e) { System.out.println("not find img.."); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } }}参考资料假设个人实战使用可以采用docker快速安装,但是未安装过FastDFS建议普通安装部署,了解一下tracker和storage的使用,以及部署搭建集群。FastDFS普通安装部署:cid:link_2FasdDFS docker安装部署:https://blog.csdn.net/weixin_44621343/article/details/117825755?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-117825755-blog-127896984.235v43pc_blog_bottom_relevance_base6&spm=1001.2101.3001.4242.1&utm_relevant_index=3OpenFeign和FastDFS的类冲突:cid:link_3Gitee地址:cid:link_1
-
Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。可以将一个 JSON 字符串转成一个 Java 对象,或者反过来。GsonUtilsimport com.google.gson.*;import com.google.gson.reflect.TypeToken;import lombok.SneakyThrows;import java.lang.reflect.Type;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.format.DateTimeFormatter;import java.util.List;import java.util.Map;import java.util.Objects;/** * @Author * @Date 2024/4 * @Des */public class GsonUtils { private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final JsonSerializer<LocalDateTime> dateTimeSerializer = (obj, type, ctx) -> new JsonPrimitive(dateTimeFormatter.format(obj)); private static final JsonSerializer<LocalDate> dateSerializer = (obj, type, ctx) -> new JsonPrimitive(dateFormatter.format(obj)); private static final JsonSerializer<LocalTime> timeSerializer = (obj, type, ctx) -> new JsonPrimitive(timeFormatter.format(obj)); private static final JsonDeserializer<LocalDateTime> dateTimeDeserializer = (json, type, ctx) -> LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), dateTimeFormatter); private static final JsonDeserializer<LocalDate> dateDeserializer = (json, type, ctx) -> LocalDate.parse(json.getAsJsonPrimitive().getAsString(), dateFormatter); private static final JsonDeserializer<LocalTime> timeDeserializer = (json, type, ctx) -> LocalTime.parse(json.getAsJsonPrimitive().getAsString(), timeFormatter); private static final Gson gson; static { GsonBuilder builder = new GsonBuilder(); builder.disableHtmlEscaping(); builder.enableComplexMapKeySerialization(); // builder.excludeFieldsWithoutExposeAnnotation(); builder.setDateFormat("yyyy-MM-dd HH:mm:ss"); builder.registerTypeAdapter(LocalDateTime.class, dateTimeSerializer); builder.registerTypeAdapter(LocalDate.class, dateSerializer); builder.registerTypeAdapter(LocalTime.class, timeSerializer); builder.registerTypeAdapter(LocalDateTime.class, dateTimeDeserializer); builder.registerTypeAdapter(LocalDate.class, dateDeserializer); builder.registerTypeAdapter(LocalTime.class, timeDeserializer); gson = builder.create(); } public static Type makeJavaType(Type rawType, Type... typeArguments) { return TypeToken.getParameterized(rawType, typeArguments).getType(); } public static String toString(Object value) { if (Objects.isNull(value)) { return null; } if (value instanceof String) { return (String) value; } return toJSONString(value); } public static String toJSONString(Object value) { return gson.toJson(value); } public static String toPrettyString(Object value) { return gson.newBuilder().setPrettyPrinting().create().toJson(value); } public static JsonElement fromJavaObject(Object value) { JsonElement result = null; if (Objects.nonNull(value) && (value instanceof String)) { result = parseObject((String) value); } else { result = gson.toJsonTree(value); } return result; } @SneakyThrows public static JsonElement parseObject(String content) { return JsonParser.parseString(content); } public static JsonElement getJsonElement(JsonObject node, String name) { return node.get(name); } public static JsonElement getJsonElement(JsonArray node, int index) { return node.get(index); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Class<T> clazz) { return gson.fromJson(node, clazz); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Type type) { return gson.fromJson(node, type); } public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) { return toJavaObject(node, typeToken.getType()); } public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) { return toJavaObject(node, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(JsonElement node) { return toJavaObject(node, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) { return toJavaObject(node, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(JsonElement node) { return toJavaObject(node, new TypeToken<Map<String, Object>>() { }.getType()); } @SneakyThrows public static <T> T toJavaObject(String content, Class<T> clazz) { return gson.fromJson(content, clazz); } @SneakyThrows public static <T> T toJavaObject(String content, Type type) { return gson.fromJson(content, type); } public static <T> T toJavaObject(String content, TypeToken<?> typeToken) { return toJavaObject(content, typeToken.getType()); } public static <E> List<E> toJavaList(String content, Class<E> clazz) { return toJavaObject(content, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(String content) { return toJavaObject(content, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) { return toJavaObject(content, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(String content) { return toJavaObject(content, new TypeToken<Map<String, Object>>() { }.getType()); }}泛型实体类import com.google.gson.*;import com.google.gson.reflect.TypeToken;import lombok.SneakyThrows;import java.lang.reflect.Type;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.format.DateTimeFormatter;import java.util.List;import java.util.Map;import java.util.Objects;/** * @Author * @Date 2024/4 * @Des */public class GsonUtils { private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final JsonSerializer<LocalDateTime> dateTimeSerializer = (obj, type, ctx) -> new JsonPrimitive(dateTimeFormatter.format(obj)); private static final JsonSerializer<LocalDate> dateSerializer = (obj, type, ctx) -> new JsonPrimitive(dateFormatter.format(obj)); private static final JsonSerializer<LocalTime> timeSerializer = (obj, type, ctx) -> new JsonPrimitive(timeFormatter.format(obj)); private static final JsonDeserializer<LocalDateTime> dateTimeDeserializer = (json, type, ctx) -> LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), dateTimeFormatter); private static final JsonDeserializer<LocalDate> dateDeserializer = (json, type, ctx) -> LocalDate.parse(json.getAsJsonPrimitive().getAsString(), dateFormatter); private static final JsonDeserializer<LocalTime> timeDeserializer = (json, type, ctx) -> LocalTime.parse(json.getAsJsonPrimitive().getAsString(), timeFormatter); private static final Gson gson; static { GsonBuilder builder = new GsonBuilder(); builder.disableHtmlEscaping(); builder.enableComplexMapKeySerialization(); // builder.excludeFieldsWithoutExposeAnnotation(); builder.setDateFormat("yyyy-MM-dd HH:mm:ss"); builder.registerTypeAdapter(LocalDateTime.class, dateTimeSerializer); builder.registerTypeAdapter(LocalDate.class, dateSerializer); builder.registerTypeAdapter(LocalTime.class, timeSerializer); builder.registerTypeAdapter(LocalDateTime.class, dateTimeDeserializer); builder.registerTypeAdapter(LocalDate.class, dateDeserializer); builder.registerTypeAdapter(LocalTime.class, timeDeserializer); gson = builder.create(); } public static Type makeJavaType(Type rawType, Type... typeArguments) { return TypeToken.getParameterized(rawType, typeArguments).getType(); } public static String toString(Object value) { if (Objects.isNull(value)) { return null; } if (value instanceof String) { return (String) value; } return toJSONString(value); } public static String toJSONString(Object value) { return gson.toJson(value); } public static String toPrettyString(Object value) { return gson.newBuilder().setPrettyPrinting().create().toJson(value); } public static JsonElement fromJavaObject(Object value) { JsonElement result = null; if (Objects.nonNull(value) && (value instanceof String)) { result = parseObject((String) value); } else { result = gson.toJsonTree(value); } return result; } @SneakyThrows public static JsonElement parseObject(String content) { return JsonParser.parseString(content); } public static JsonElement getJsonElement(JsonObject node, String name) { return node.get(name); } public static JsonElement getJsonElement(JsonArray node, int index) { return node.get(index); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Class<T> clazz) { return gson.fromJson(node, clazz); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Type type) { return gson.fromJson(node, type); } public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) { return toJavaObject(node, typeToken.getType()); } public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) { return toJavaObject(node, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(JsonElement node) { return toJavaObject(node, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) { return toJavaObject(node, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(JsonElement node) { return toJavaObject(node, new TypeToken<Map<String, Object>>() { }.getType()); } @SneakyThrows public static <T> T toJavaObject(String content, Class<T> clazz) { return gson.fromJson(content, clazz); } @SneakyThrows public static <T> T toJavaObject(String content, Type type) { return gson.fromJson(content, type); } public static <T> T toJavaObject(String content, TypeToken<?> typeToken) { return toJavaObject(content, typeToken.getType()); } public static <E> List<E> toJavaList(String content, Class<E> clazz) { return toJavaObject(content, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(String content) { return toJavaObject(content, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) { return toJavaObject(content, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(String content) { return toJavaObject(content, new TypeToken<Map<String, Object>>() { }.getType()); }}示例//Result<String>:Result<String> source = GsonUtils.toJavaObject(c, new TypeToken<Result<String>>() {});String userStatus = Result.getSuccessResult(source, "");//Result<User>:Result<User> source = GsonUtils.toJavaObject(c, new TypeToken<Result<User>>() {});User userInfo = Result.getSuccessResult(source, null);//Result<List<Blog>>:Result<List<Blog>> source = GsonUtils.toJavaObject(c, new TypeToken<Result<List<Blog>>>() {});List<Blog> blogList = Result.getSuccessResult(source, Collections.emptyList());//Result<Map<String, Integer>>:Result<Map<String, Integer>> source = GsonUtils.toJavaObject(c, new TypeToken<Result<Map<String, Integer>>>() { });Map<String, Integer> statistics = Result.getSuccessResult(source, Collections.EMPTY_MAP);
-
前两天看前辈们的老代码,看到了一句神奇的SQL,生平第一次见: select ……………… from (select xx1,xx2,xx3,……,row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum from xxxx_tbl where xxx8='sdfdsf' ……) where rownum =1 为防止公司说泄露源码,就只能这样表示一下意思了,这句sql的灵魂之处在于row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc),你们见没见过我不知道,反正我以前没见过。所以一度没看懂。然后就一探究竟了,为了更加直观,我们还是拿前一篇limit重复问题里的那种表来进行举例。 案例 表结构如下: 字段 类型 注释 id varchar(20) 主键 col1 varchar(20) col1 col2 varchar(20) col2 col3 varchar(20) col3 全表查询: SELECT * FROM test1 ORDER BY col1 DESC; 数据为: id col1 col2 col3 15 5 9 10 12 2 5 6 14 2 7 8 16 2 4 5 11 1 2 3 其中col1字段不是唯一的,第二第三第四行的col1都是2。 上关键sql: SELECT id,col1,col2,col3, row_number() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; 返回结果: id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 我们看这个返回结果,其中row_num列即为这段神奇的sql产生的,这一列的序号怎么来的呢:他是按照col1进行分组,然后按照id列进行倒序排序,row_num为分组后组内排序的结果。 分析 看完案例中的sql和执行结果,其实我们就能猜出开头的那个sql的目的了,他是要获取分组后每组的第一个值。其实我们经常会遇到这样的案例,比如:给你一个全年级学生的分数表,我要获取每个班分数最高的前三名。如果说这个年级有多少个班是已知的,我们可以通过union一个一个子查询拼接起来,但是如果班级个数未知,那这时候如果想用一句sql就有点无奈了。 同样,如果我们想要获取的是每个班级分数最高的一个人,我们也可以通过group by加max函数再加子查询解决,但是这里不是一个。 partition by与group by 一开始其实没搞明白,同样是分组partition by和group by有什么区别。从用法上来看: partition by select xx1,xx2,xx3,……,row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum from xxxx_tbl group by select xx1,max(xx2) from xxxx_tbl group by xx1 partition by是用在返回参数中的,而group by是用在约束里的。而深层里去理解,partition by是分组后进行组内逐条分析,比如这里的row_number() over,而groupy by则是分组后进行整组的聚合分析,比如上面的max()。 扩展 既然是用于分析的,肯定有一些常用的与之配合的分析函数,比如group常和sum、min、max等组合使用。partition by除了上面的row_number外还有以下一些常用的配合: max:获取组内已排序的最大值 SELECT id,col1,col2,col3, MAX(col3) over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 3 16 2 4 5 5 14 2 7 8 8 12 2 5 6 8 15 5 9 10 10 rank:排名的时候用row_number不是很好,原因是如果有两行order by的id相同,那么row_number就会漏掉其中的一行,而rank则不会漏,同时rank是跳跃排名,比如有两个第二名,那第四个就是第四名,而不是第三名 SELECT id,col1,col2,col3, rank() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 dense_rank:和rank一样,也是能够查出所有的记录,但是他不是跳跃排名,两个第二名之后是第三名。 SELECT id,col1,col2,col3, dense_rank() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 ———————————————— 原文链接:https://blog.csdn.net/qq_30095631/article/details/103558652
-
partition 子句This article will cover the SQL PARTITION BY clause and, in particular, the difference with GROUP BY in a select statement. We will also explore various use case of SQL PARTITION BY.本文将介绍SQL PARTITION BY子句,尤其是select语句中与GROUP BY的区别。 我们还将探讨SQL PARTITION BY的各种用例。We use SQL PARTITION BY to divide the result set into partitions and perform computation on each subset of partitioned data.我们使用SQL PARTITION BY将结果集划分为多个分区,并对分区数据的每个子集执行计算。准备样品数据 (Preparing Sample Data )Let us create an Orders table in my sample database SQLShackDemo and insert records to write further queries.让我们在示例数据库SQLShackDemo中创建一个Orders表,并插入记录以编写进一步的查询。Use SQLShackDemoGoCREATE TABLE [dbo].[Orders]( [orderid] INT, [Orderdate] DATE, [CustomerName] VARCHAR(100), [Customercity] VARCHAR(100), [Orderamount] MONEY)I use ApexSQL Generate to insert sample data into this article. Right click on the Orders table and Generate test data.我使用ApexSQL Generate将示例数据插入本文。 右键单击“订单”表并生成测试数据 。It launches the ApexSQL Generate. I generated a script to insert data into the Orders table. Execute this script to insert 100 records in the Orders table.它启动ApexSQL生成。 我生成了一个脚本,用于将数据插入到Orders表中。 执行此脚本以在Orders表中插入100条记录。USE [SQLShackDemo]GOINSERT [dbo].[Orders] VALUES (216090, CAST(N'1826-12-19' AS Date), N'Edward', N'Phoenix', 4713.8900)GOINSERT [dbo].[Orders] VALUES (508220, CAST(N'1826-12-09' AS Date), N'Aria', N'San Francisco', 9832.7200)GO…Once we execute insert statements, we can see the data in the Orders table in the following image.执行插入语句后,我们可以在下图中的Orders表中看到数据。We use SQL GROUP BY clause to group results by specified column and use aggregate functions such as Avg(), Min(), Max() to calculate required values.我们使用SQL GROUP BY子句按指定的列对结果进行分组,并使用诸如Avg(),Min(),Max()之类的聚合函数来计算所需的值。按功能分组 (Group By function syntax)SELECT expression, aggregate function ()FROM tablesWHERE conditionsGROUP BY expressionSuppose we want to find the following values in the Orders table假设我们要在“订单”表中找到以下值Minimum order value in a city一个城市的最小订单价值Maximum order value in a city城市中的最大订单价值Average order value in a city一个城市的平均订单价值Execute the following query with GROUP BY clause to calculate these values.使用GROUP BY子句执行以下查询以计算这些值。SELECT Customercity, AVG(Orderamount) AS AvgOrderAmount, MIN(OrderAmount) AS MinOrderAmount, SUM(Orderamount) TotalOrderAmountFROM [dbo].[Orders]GROUP BY Customercity;In the following screenshot, we can see Average, Minimum and maximum values grouped by CustomerCity.在以下屏幕截图中,我们可以看到按CustomerCity分组的平均值,最小值和最大值。Now, we want to add CustomerName and OrderAmount column as well in the output. Let’s add these columns in the select statement and execute the following code.现在,我们要在输出中也添加CustomerName和OrderAmount列。 让我们将这些列添加到select语句中,并执行以下代码。 SELECT Customercity, CustomerName ,OrderAmount, AVG(Orderamount) AS AvgOrderAmount, MIN(OrderAmount) AS MinOrderAmount, SUM(Orderamount) TotalOrderAmountFROM [dbo].[Orders]GROUP BY Customercity;Once we execute this query, we get an error message. In the SQL GROUP BY clause, we can use a column in the select statement if it is used in Group by clause as well. It does not allow any column in the select clause that is not part of GROUP BY clause.一旦执行此查询,我们将收到一条错误消息。 在SQL GROUP BY子句中,如果同时在Group by子句中使用它,则可以在select语句中使用一列。 它不允许select子句中的任何列不属于GROUP BY子句。We can use the SQL PARTITION BY clause to resolve this issue. Let us explore it further in the next section.我们可以使用SQL PARTITION BY子句解决此问题。 让我们在下一部分中进一步探讨它。SQL分区依据 (SQL PARTITION BY)We can use the SQL PARTITION BY clause with the OVER clause to specify the column on which we need to perform aggregation. In the previous example, we used Group By with CustomerCity column and calculated average, minimum and maximum values.我们可以将SQL PARTITION BY子句与OVER子句一起使用,以指定需要对其进行聚合的列。 在上一个示例中,我们将“分组依据”与“ CustomerCity”一起使用,并计算了平均值,最小值和最大值。Let us rerun this scenario with the SQL PARTITION BY clause using the following query.让我们使用以下查询,使用SQL PARTITION BY子句重新运行此方案。SELECT Customercity, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];In the output, we get aggregated values similar to a GROUP By clause. You might notice a difference in output of the SQL PARTITION BY and GROUP BY clause output.在输出中,我们获得类似于GROUP BY子句的聚合值。 您可能会注意到SQL PARTITION BY和GROUP BY子句输出的输出有所不同。Group BySQL PARTITION BYWe get a limited number of records using the Group By clauseWe get all records in a table using the PARTITION BY clause.It gives one row per group in result set. For example, we get a result for each group of CustomerCity in the GROUP BY clause.It gives aggregated columns with each record in the specified table.We have 15 records in the Orders table. In the query output of SQL PARTITION BY, we also get 15 rows along with Min, Max and average values.通过...分组SQL分区依据我们使用Group By子句获得的记录数量有限我们使用PARTITION BY子句获取表中的所有记录。它在结果集中为每组一行。 例如,我们在GROUP BY子句中为CustomerCity的每个组获取结果。它为指定表中的每条记录提供汇总列。我们在订单表中有15条记录。 在SQL PARTITION BY的查询输出中,我们还获得15行以及Min,Max和平均值。In the previous example, we get an error message if we try to add a column that is not a part of the GROUP BY clause.在上一个示例中,如果尝试添加不属于GROUP BY子句的列,则会收到一条错误消息。We can add required columns in a select statement with the SQL PARTITION BY clause. Let us add CustomerName and OrderAmount columns and execute the following query.我们可以使用SQL PARTITION BY子句在select语句中添加必需的列。 让我们添加CustomerName和OrderAmount列并执行以下查询。SELECT Customercity, CustomerName, OrderAmount, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];We get CustomerName and OrderAmount column along with the output of the aggregated function. We also get all rows available in the Orders table.我们获得CustomerName和OrderAmount列以及聚合函数的输出。 我们还将在Orders表中获得所有可用行。In the following screenshot, you can for CustomerCity Chicago, it performs aggregations (Avg, Min and Max) and gives values in respective columns.在以下屏幕截图中,您可以为CustomerCity Chicago进行聚合(平均,最小和最大)并在相应的列中提供值。Similarly, we can use other aggregate functions such as count to find out total no of orders in a particular city with the SQL PARTITION BY clause.同样,我们可以使用其他聚合函数(例如count)来通过SQL PARTITION BY子句找出特定城市的订单总数。SELECT Customercity, CustomerName, OrderAmount, COUNT(OrderID) OVER(PARTITION BY Customercity) AS CountOfOrders, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];We can see order counts for a particular city. For example, we have two orders from Austin city therefore; it shows value 2 in CountofOrders column.我们可以看到特定城市的订单计数。 例如,因此,我们有两个来自奥斯丁市的订单; 它在CountofOrders列中显示值2。带有ROW_NUMBER()的PARTITION BY子句 (PARTITION BY clause with ROW_NUMBER())We can use the SQL PARTITION BY clause with ROW_NUMBER() function to have a row number of each row. We define the following parameters to use ROW_NUMBER with the SQL PARTITION BY clause.我们可以将SQL PARTITION BY子句与ROW_NUMBER()函数一起使用,以获取每行的行号。 我们定义以下参数以将ROW_NUMBER与SQL PARTITION BY子句一起使用。PARTITION BY column – In this example, we want to partition data on PARTITION BY列 –在此示例中,我们要对CustomerCity column CustomerCity列上的数据进行分区Order By: In the ORDER BY column, we define a column or condition that defines row number. In this example, we want to sort data on the OrderAmount column排序依据:在ORDER BY列中,我们定义一列或条件来定义行号。 在此示例中,我们要对OrderAmount列上的数据进行排序SELECT Customercity, CustomerName, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", OrderAmount, COUNT(OrderID) OVER(PARTITION BY Customercity) AS CountOfOrders, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];In the following screenshot, we get see for CustomerCity Chicago, we have Row number 1 for order with highest amount 7577.90. it provides row number with descending OrderAmount.在以下屏幕截图中,我们看到了CustomerCity Chicago ,我们的订单行1为最高金额7577.90。 它为行号提供降序的OrderAmount。具有累积总值的PARTITION BY子句 (PARTITION BY clause with Cumulative total value)Suppose we want to get a cumulative total for the orders in a partition. Cumulative total should be of the current row and the following row in the partition.假设我们要获得分区中订单的累计总数。 累积总数应该是分区中当前行和下一行的总和。For example, in the Chicago city, we have four orders.例如,在芝加哥市,我们有四个订单。CustomerCityCustomerNameRankOrderAmountCumulative Total RowsCumulative TotalChicagoMarvin17577.9Rank 1 +214777.51ChicagoLawrence27199.61Rank 2+314047.21ChicagoAlex36847.66Rank 3+48691.49ChicagoJerome41843.83Rank 41843.83客户城市顾客姓名秩订单金额累积总行累计总数芝加哥Maven1个7577.9等级1 +214777.51芝加哥劳伦斯27199.61等级2 + 314047.21芝加哥亚历克斯36847.66等级3 + 48691.49芝加哥杰罗姆41843.83等级41843.83In the following query, we the specified ROWS clause to select the current row (using CURRENT ROW) and next row (using 1 FOLLOWING). It further calculates sum on those rows using sum(Orderamount) with a partition on CustomerCity ( using OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC).在下面的查询中,我们指定了ROWS子句以选择当前行(使用CURRENT ROW)和下一行(使用1 FOLLOWING)。 它还使用sum(Orderamount)和CustomerCity上的分区(使用OVER(PARTITION BY Customercity或ORDER BY OrderAmount DESC))来计算这些行上的总和。SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), SUM(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), 1) AS CumulativeTotal,Similarly, we can calculate the cumulative average using the following query with the SQL PARTITION BY clause.同样,我们可以使用带有SQL PARTITION BY子句的以下查询来计算累积平均值。 SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), AVG(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), 1) AS CumulativeAVG用PARTITION BY子句限制行的行数 (ROWS UNBOUNDED PRECEDING with the PARTITION BY clause)We can use ROWS UNBOUNDED PRECEDING with the SQL PARTITION BY clause to select a row in a partition before the current row and the highest value row after current row.我们可以将ROWS UNBOUNDED PRECEDING与SQL PARTITION BY子句一起使用,以选择分区中当前行之前的行以及当前行之后的最大值行。In the following table, we can see for row 1; it does not have any row with a high value in this partition. Therefore, Cumulative average value is the same as of row 1 OrderAmount.在下表中,我们可以看到第1行; 该分区中没有任何具有高值的行。 因此,累积平均值与第1行OrderAmount相同。For Row2, It looks for current row value (7199.61) and highest value row 1(7577.9). It calculates the average for these two amounts.对于第2行,它将查找当前行值(7199.61)和最高值行1(7577.9)。 它计算这两个数量的平均值。For Row 3, it looks for current value (6847.66) and higher amount value than this value that is 7199.61 and 7577.90. It calculates the average of these and returns.对于第3行,它将查找当前值(6847.66)和比该值更高的金额值7199.61和7577.90。 它计算这些平均值并返回。CustomerCityCustomerNameRankOrderAmountCumulative Average RowsCumulative AverageChicagoMarvin17577.9Rank 17577.90ChicagoLawrence27199.61Rank 1+27388.76ChicagoAlex36847.66Rank 1+2+37208.39ChicagoJerome41843.83Rank 1+2+3+45867.25客户城市顾客姓名秩订单金额累积平均行累积平均值芝加哥Maven1个7577.9等级17577.90芝加哥劳伦斯27199.61等级1 + 27388.76芝加哥亚历克斯36847.66等级1 + 2 + 37208.39芝加哥杰罗姆41843.83等级1 + 2 + 3 + 45867.25Execute the following query to get this result with our sample data.执行以下查询以通过我们的样本数据获得此结果。 SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), AVG(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS UNBOUNDED PRECEDING), 1) AS CumulativeAvgFROM [dbo].[Orders];结论 (Conclusion)In this article, we explored the SQL PARTIION BY clause and its comparison with GROUP BY clause. We also learned its usage with a few examples. I hope you find this article useful and feel free to ask any questions in the comments below在本文中,我们探讨了SQL PARTIION BY子句及其与GROUP BY子句的比较。 我们还通过一些示例了解了它的用法。 希望本文对您有所帮助,并随时在下面的评论中提问原文链接:https://blog.csdn.net/culuo4781/article/details/107618029
-
1.分表与表分区的区别 1.1 关于分表 分表是将一个大表分为几个或是多个小表,例如:table_1每天有1Kw的数据量,table_1随便时间的增长会越来越大,最终达到mysql表的极限,在这种比较极端的情况下 我们可以考虑对table_01进行分表操作,即每天生成与table_1表同样的表,每天一张即table_1_20120920 更多详细:http://blog.51yip.com/mysql/949.html 1.2 关于分区 以myisam为例子,mysql数据库中的数据是以文件的形势存在磁盘上,一张表主要对应着三个文件,一个是frm存放表结构文件,一个存放表数据的,一个是myi存表索引。 也就是将一个表文件分为多个表文件在磁盘上进行存取,提高对io的使用。 1.3 是否支持分区 mysql> show variables like ‘%partition%’; +——————-+——-+ | Variable_name | Value | +——————-+——-+ | have_partitioning | YES | +——————-+——-+ 出现YES表示当前版本支持表分区 1.4 查看分区表信息 select * from INFORMATION_SCHEMA.PARTITIONS where TABLE_SCHEMA=’tablename’ 2.如何分区 2.1 分区方法 分区有二个方法: 水平分区、垂直分区 2.2 分区的类型 === 水平分区的几种模式:=== * Range(范围) – 这种模式允许DBA将数据划分不同范围。例如DBA可以将一个表通过年份划分成三个分区,80年代(1980′s)的数据,90年代(1990′s)的数据以及任何在2000年(包括2000年)后的数据。 * Hash(哈希) – 这中模式允许DBA通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区,。例如DBA可以建立一个对表主键进行分区的表。 * Key(键值) – 上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。 * List(预定义列表) – 这种模式允许系统通过DBA定义的列表的值所对应的行数据进行分割。例如:DBA建立了一个横跨三个分区的表,分别根据2004年2005年和2006年值所对应的数据。 * Composite(复合模式) – 很神秘吧,哈哈,其实是以上模式的组合使用而已,就不解释了。举例:在初始化已经进行了Range范围分区的表上,我们可以对其中一个分区再进行hash哈希分区。 = 垂直分区(按列分)= 举个简单例子:一个包含了大text和BLOB列的表,这些text和BLOB列又不经常被访问,这时候就要把这些不经常使用的text和BLOB了划分到另一个分区,在保证它们数据相关性的同时还能提高访问速度。 2.2 代码演示 range分区如下: –按天进行划分 错误代码 create table part_range ( id bigint not null auto_increment, ftime date, str text )engine=myisam partition by range (ftime) ( partition p0 values less than (to_days(’2012-09-21′)), partition p1 values less than (to_days(’2012-09-22′)) ) 错误原因: 1. 在partition by range (ftime),ftime需要加to_days转成数字 2.进行分区的字段需要是主键的一部分1 使用以上语句创建时报错 ‘A PRIMARY KEY must include all columns in the table’s partitioning function” 默认分区限制分区字段必须是主键(PRIMARY KEY)的一部分 –按天进行分区 –直接使用时间列不可以,RANGE分区函数返回的列需要是整型。 create table part_range ( id bigint not null auto_increment, ftime date, str text, primary key(id,ftime) )engine=myisam partition by range (to_days(ftime)) ( partition p0 values less than (to_days(’2012-09-21′)), partition p1 values less than (to_days(’2012-09-22′)), PARTITION p3 VALUES LESS THAN MAXVALUE ); –按小时进行分区 create table part_range_day ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range(hour(ftime)) ( partition p0 values less than (1), partition p1 values less than(2), PARTITION p3 VALUES LESS THAN MAXVALUE ) [Err] 1493 – VALUES LESS THAN value must be strictly increasing for each partition 使用id进行划分时 –按id进行划分,id只能是由小到大 create table part_range_id ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range (id) ( partition p0 values less than (10000), partition p1 values less than (20000), partition p2 values less than maxvalue ) –使用list create table part_range_list ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by list (id) ( partition p0 values in (0,1), partition p1 values in (2,4) ) –5.5之后的mysql说可以支持字符目前使用的5.5.24版本,使用字符时依然提示 [Err] 1697 – VALUES value for partition ‘p0′ must have type INT 3.性能测试 硬件:3.6G 内存 cpu Intel(R) Pentiun(R) 软件:win xp2 32位 –没有加分区表part_no_test,myisam引擎 create table part_no_test ( id bigint primary key auto_increment, ftime datetime, str text )engine=myisam –加入分区的表part_test,myisam引擎,以小时划分四个区 create table part_test ( id bigint auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range(hour(ftime)) ( partition p0 values less than (6), partition p1 values less than (12), partition p3 values less than (18), partition p4 values less than maxvalue ) –随机数据构造,构造2kw数据量 INSERT INTO part_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),’sss’); 生成数据存储过程 —生成part_no_test数据 drop procedure if exists part_no_insert_data; create procedure part_insert_data() begin set @id=20000000; while @id>0 do INSERT INTO part_no_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),RAND()*RAND()*100000000000); set @id=@id-1; end while; end; —生成part_test数据 drop procedure if exists part_insert_data; create procedure part_insert_data() begin set @id=20000000; while @id>0 do INSERT INTO part_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),RAND()*RAND()*100000000000); set @id=@id-1; end while; end; –有分区查询语句 select * from part_test a where a.ftime>’2012-09-20 10:00:00′ and a.ftime<’2012-09-20 12:00:00′; 执行时间:09.906s –无分区查询语句 select * from part_no_test a where a.ftime>’2012-09-20 10:00:00′ and a.ftime<’2012-09-20 12:00:00′; 执行时间:23.281s 附: Mysql可用的分区函数 DAY() DAYOFMONTH() DAYOFWEEK() DAYOFYEAR() DATEDIFF() EXTRACT() HOUR() MICROSECOND() MINUTE() MOD() MONTH() QUARTER() SECOND() TIME_TO_SEC() TO_DAYS() WEEKDAY() YEAR() YEARWEEK() 等 当然,还有FLOOR(),CEILING() 等,前提是使用这两个分区函数的分区健必须是整型。 要小心使用其中的一些函数,避免犯逻辑性的错误,引起全表扫描。 注: 1.分区的新增、删除每次只能是一个 2.maxvalues 后面不能再加分区 3.分区键必须包含在主键中 ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table’s partitioning function’ 4.ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table’s partitioning function’说明在表上建约束索引(如唯一索引,普通索引可以)会有问题,必须把约束索引列包含在分区健内 5.只有RANGE和LIST分区才能有子分区,每个分区的子分区数量必须相同, 6. MYSQL将NULL值视为0.自动插入最小的分区中。 = 初步结论 = * 分区和未分区占用文件空间大致相同 (数据和索引文件) * 如果查询语句中有未建立索引字段,分区时间远远优于未分区时间 * 如果查询语句中字段建立了索引,分区和未分区的差别缩小,分区略优于未分区。 = 最终结论 = * 对于大数据量,建议使用分区功能。 * 去除不必要的字段 * 根据手册, 增加myisam_max_sort_file_size 会增加分区性能 ———————————————— 原文链接:https://blog.csdn.net/ls3648098/article/details/9353623
-
一、窗口函数的作用窗口函数是对一组值进行操作,不需要使用GROUP BY 子句对数据进行分组,还能够在同一行中同时返回基础行的列和聚合列。窗口函数,基础列和聚合列的查询都非常简单。二、语法格式 窗口函数的语法格式如下:1OVER([PARTITION BY value_expression,..[n] ] < ORDER BY BY_Clause>)PARTITION:分组;ORDER BY:排序;首先建一张调试表如下:123456CREATE TABLE [dbo].[xxx]([Id] [ int ] NULL ,[ Name ] [nvarchar](50) NULL ,[Operate] [nvarchar](50) NULL ,[Score] [ int ] NULL ,[CreateTime] [datetime] NULL ) ON [ PRIMARY ]往里面添加如下数据。三、应用场景1、聚合列与数据列共同显示12--查询姓名、分数、以及全体平均分SELECT Name , Score, CAST ( AVG (Score) OVER() AS decimal (5,2) ) AS '平均分' FROM xxx 2、分组日期最新1234--对每个人查询日期最新列SELECT * FROM ( SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) AS part ,Score, Name , CreateTime FROM xxx) AS CWHERE C.part = 13、分页返回结果集内的行号,每个分区从1开始,ORDER BY可确定在特定分区中为行分配唯一 ROW_NUMBER 的顺序。四、排名函数1、ROW_NUMBER()返回结果集内的行号,每个分区从1开始计算,ORDER BY可确定在特定分区中为行分配唯一 ROW_NUMBER 的顺序。1SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) ,Score, Name , CreateTime FROM xxx输出如下:2、RANK()返回结果集的分区内每行的排序。行的排名是从1开始算。如果两个或多个行与一个排名关联,则每个关联行将得到相同的排名。1SELECT RANK() OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name , CreateTime FROM xxx 下面一张图片很好地说明了Rank与ROW_NUMBER的区别。 3、DENSE_RANK() 返回结果集分区中行的排名,与Rank()类似,只是对并列的处理稍有不同,详见示例。SELECT DENSE_RANK() OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name, CreateTimeFROM xxx 下面的示例展示了Rank()与Dense_Rank()的区别。 4、NTILE() NTILE函数把结果中的行关联到组,并为每一行分配一个所属的组的编号,编号从1开始。对于每一个行,NTILE 将返回此行所属的组的编号。 如果分区的行数不能被 integer_expression(就是传入的那个参数,表示分几个组的意思) 整除,则将导致一个成员有两种大小不同的组。按照 OVER 子句指定的顺序,较大的组排在较小的组前面。--每个分区分2个组,该列是改行所属的组名SELECT NTILE(2) OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name, CreateTimeFROM xxx原文链接:https://blog.csdn.net/xoopx/article/details/51777733
-
1.分页查询 ROW_NUMBER() OVER(ORDER BY COLUMMS) IF OBJECT_ID('SysLogInfo','u') IS NULL --不存在用户表 BEGIN CREATE TABLE SysLogInfo( ID NVARCHAR(50) NOT NULL PRIMARY KEY, Name NVARCHAR(50) NULL, OldName NVARCHAR(50) NULL ) INSERT INTO dbo.SysLogInfo( ID, Name, OldName ) VALUES ( '1','张三','张二'), ( '2','李四','张二'), ( '3','赵武','赵六'), ( '4','钱7','赵六') END --分页查询 DECLARE @page INT =1 DECLARE @pageSize INT =2 SELECT ID,Name,OldName FROM ( SELECT *,ROW_NUMBER() OVER (ORDER BY ID asc) row FROM dbo.SysLogInfo) a WHERE row BETWEEN (@page-1)*@pageSize + 1 AND @page * @pageSize 2.ROW_NUMBER() OVER (PARTITION BY COL1 ORDER BY COL2) 可以返回一个分组的多条记录 SELECT *,ROW_NUMBER() OVER (PARTITION BY OldName ORDER BY ID ) COUNT FROM dbo.SysLogInfo 结果: partition by关键字是分析性函数的一部分,它和聚合函数(如group by)不同的地方在于它能返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的记录。 partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。 partition by 与group by不同之处在于前者返回的是分组里的每一条数据,并且可以对分组数据进行排序操作。后者只能返回聚合之后的组的数据统计值的记录。 ———————————————— 原文链接:https://blog.csdn.net/wodemingzijiaoke/article/details/90612385
-
ROW_NUMBER() OVER(…) 虽然写法很复杂,但这确实只是一个普通函数(就像字符串转数字这样的函数),可以得出一个值 这个函数不会改变数据条数,它的作用是给每个数据记录增加一个字段,这个字段值就是函数得到的值 函数虽然不会改变查询结果条数,但会改变结果的顺序。会按照某个字段a(这个字段会重复)分组,函数的值就是在每个分组内部的排序值(1,2,3,4.。。) 比如:一群人在排队买东西。会自动以家庭为单位分成一段一段的“小组”,然后又在家庭内部按照年龄大小做了排序(比如年龄大人在前面,小孩跟在大人后面),这个函数值就是这个家庭内部排序值 按照上面的比喻,这个函数就是ROW_NUMBER() over (PARTITION BY 家庭编号 order by 年龄) 函数完整的样子:ROW_NUMBER() OVER(PARTITION BY… ORDER BY…)。为什么要很啰嗦的再加一个ROW_NUMBER()? 因为前面这个ROW_NUMBER()会进一步控制函数的特性,后面会讲解【不过我个人认为这个前缀确实很啰嗦。让我设计的话,我会去掉它。因为对于大部分人可能就只会用最普通的函数使用方式,不写时就默认为ROW_NUMBER()多好】 类比一个完整的查询 select *, ROW_NUMBER() over (PARTITION BY 家庭编号 order by 年龄 desc) as “家庭地位” from 人员表 这句话除了得到了所有人员的信息之外,还额外得到了一个字段"家庭地位"(这里就默认:年龄越大,家庭地位越高) 理解了函数的含义和用法,然后我们就可以利用函数,比如,只获取所有家庭地位最高的人,那么就可以在我们再包一层,得到: select * from( select *, ROW_NUMBER() over (PARTITION BY 家庭编号 order by 年龄 desc) as “家庭地位” from 人员表 ) t where t.家庭地位=1 引申 这样就可以解决类似:表T中 字段a会重复,但我们查询的结果又不想要重复的数据,并且要求只要其中最新的那一条 select * from( select *, ROW_NUMBER() over (PARTITION BY a order by create_time desc) as index from T ) t where t.index=1 或者topN问题:分组后,把每组前5个找出来 select * from( select *, ROW_NUMBER() over (PARTITION BY a order by create_time desc) as index from T ) t where t.index < 6 ROW_NUMBER() 这是一个控制 OVER函数特性的参数,而且不能省,over前面必须有一个。但并不是只有ROW_NUMBER()这一种。 假设在一共家庭中,有四个孩子,其中两个是双胞胎,年龄分别是5岁,7岁,7岁,9岁。 两个7岁的双胞胎 谁大谁小是很难说。理论上可以讲,两个孩子年龄是相同的。那么排队时,谁是老大谁是老二呢。 ROW_NUMBER():简单粗暴的做了自己的判断,哪条数据在前面,哪个就是老大(谁先生出来谁就是老大)。即便他们年龄一样(over函数值一样),它给这几个孩子定的家庭地位分别为4,3,2,1 DENSE_RENK():显得更公正一点:既然定好了按年龄排序,那么年龄相同,地位就是相同。它给四个孩子定的家庭地位分别为:3,2,2,1 RENK():比较特殊,它觉得那个5岁的孩子的家庭地位不应该是第3位。因为家里明明有4个孩子,它是最小的,两个并列第二之后,下一个应该是4(所以直接把3给跳过了)。所以它给的家庭地位分别是4,2,2,1 名称 下面这些都是这个函数的名称: 窗口函数,分析函数,分区函数,一般称为窗口函数(window function)。 一般关系型数据库都支持。由于属于比较高级的函数,都是在数据库不断完善的过程中增加的。比如mysql就是8.x之后才有。 和聚合函数对比 聚合函数:sum(), avg() 等统计函数 配合 group by 称为聚合函数 得出的是分组后 每一组等统计数据,改变了数据条数 如果想保持数据原有的样子,则需要使用窗口函数 窗口函数前面除了前面介绍的常用的三种,还可以使用sum() avg()等统计函数,变成: sum() over(partition by ...) x 这里得到的x,就是对这个组内的求和,组内每一条数据都得到一个相同的值 进阶-和排序函数对比 虽然前面说窗口函数的核心是partition by,但实际上partition by也可以去掉。 也就是说:xxx() over(…) 这个函数非常灵活。 其中partition by也可去掉,但此时必须要有order by。 变成:xxx() over(order by …) xxx 此时窗口函数 的窗口就只有一个了:所有数据都在同一个窗口里。 只剩下排序功能,比如 dense_renk() over(over by 成绩) 名次 常见作用:给班级学生成绩排名,成绩一样的,名次就一样 ———————————————— 原文链接:https://blog.csdn.net/yunduanyou/article/details/122583303
-
1.窗口函数 (1)partition by窗口函数 和 group by分组的区别: partition by关键字是分析性函数的一部分,它和聚合函数(如group by)不同的地方在于它能返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的记录。 partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。 partition by与group by不同之处在于前者返回的是分组里的每一条数据,并且可以对分组数据进行排序操作。后者只能返回聚合之后的组的数据统计值的记录。 partition by相比较于group by,能够在保留全部数据的基础上,只对其中某些字段做分组排序(类似excel中的操作),而group by则只保留参与分组的字段和聚合函数的结果; 简单来说窗口函数对部分数据进行排序、计算等操作,group by对一组值进行聚合,即窗口函数是每一行都会保留,group by是从多行浓缩为少数行。 (2)窗口函数基本语法 <窗口函数> over ( partition by<用于分组的列名> order by <用于排序的列名>) (3)窗口函数 专用窗口函数: rank(), dense_rank(), row_number() 聚合函数 : sum(), max(), min(), count(), avg() 等 2.窗口函数的使用 2.1 over函数的写法: over(partition by type order by price desc) --先对 type 中相同的进行分区,在 type 中相同的情况下对 price 进行排序 2.2 专用窗口函数 rank() 和 row_number() 以及 dense_rank() SELECT *,rank() over(partition by type order by price desc) as mm from commodity; SELECT *,row_number() over(partition by type order by price desc) as mm from commodity; SELECT *,dense_rank() over(partition by type order by price desc) as mm from commodity; 从以上结果来看: rank()函数:如果存在并列名次的行,会占用下一个名次的位置,比如苹果的组内排名 1,2,3,4, 但是由于有两个是并列的,所以显示的排名是 1,1,3,4 ,其中 2 的位置还是被占用了 row_number()函数:不考虑并列的情况,此函数即使遇到了price 相同的情况,还是会默认排出一个先后来 dense_rank()函数:如果存在并列名次的行,不会占用下一个名次的位置,例如图片的最后显示的是 1,1,2,3 2.3 聚合函数作为窗口函数 (1) sum() SELECT *,sum(price) over(partition by type order by price) as sum from commodity; 在进行求和的时候是这样的,当前行的 sum 值是组内当前行与其组内当前行之前所有行的和,例如红色圈出来不的数据,橙子第一行是 6 ,第二行是 两行的和 6 +8 = 14,同样的红色圈出来的 苹果的也是同样的道理。需要注意的是当在排序的时候出现相同的时候,同样的都是 12 或者 同样的都是 5 无法进行区分,所以在计算的时候会把两个或多个值都加进去,这样也就是 橙色圈出来的部分了 从 8 --> 8+10 = 18 --> 18+12+12 = 42 -->18+12+12 = 42 ,大概就是这个意思,下文会告诉大家如何解决这种问题 (rows between unbounded preceding and current row) 我们来多看几种排序的结果是否符合上面的描述: -- order by type SELECT *,sum(price) over(partition by type order by type) as sum from commodity; -- order by position SELECT *,sum(price) over(partition by type order by position) as sum from commodity; -- order by id SELECT *,sum(price) over(partition by type order by id) as sum from commodity; (2) max(), min(), avg(), count() SELECT *,sum(price) over(partition by type order by price) as sum, max(price) over(partition by type order by price) as max, min(price) over(partition by type order by price) as min, avg(price) over(partition by type order by price) as avg, count(price) over(partition by type order by price) as count from commodity; 我们可以看的到, 不管是sum(), avg() 还是min(), max(), count() 他们在窗口函数中,都是对自身记录以及位于自身记录之前的数据进行聚合,求和、求平均、最小值、最大值等。所以,聚合函数作为窗口函数的时候可以在每一行的数据里直观的看到,截止到本行数据统计数据是多少,也可以看出每一行数据对整体的影响。(注意 : 数据重复的除外,有点特殊)也就是说 sum(), max(), min(), avg(), count() 都是类似的。 2.4 rows 与 range rows是物理窗口,即根据order by 子句排序后,取的前N行及后N行的数据计算(与当前行的值无关,只与排序后的行号相关) range是逻辑窗口,是指定当前行对应值的范围取值,列数不固定,只要行值在范围内,对应列都包含在内 通俗点来讲就是说:rows 取的时候是取当前行的前几行以及后几行,包括当前行在内一起进行计算的;而 range 不受行的限制,他跟当前行的值有关,当前行的值减去几,加上几,这个范围内的值都是要进行计算的数据,具体例子如下所示: --在当前行往前1行,往后2行,一共4行范围内进行计算 rows between 1 preceding and 2 following --在当前行的数值往前1个数值,往后2个数值,进行计算,范围不一定,因为可能会出现重复值 range between 1 preceding and 2 following --rows SELECT *,sum(price) over(partition by type order by price rows between 1 preceding and 2 following) as sum from commodity where type = '苹果'; 第一行 8 ,前一行没有,后两行是 10,12 --> 8 + 10 + 12 = 30 第二行是 10 ,前一行 8,后两行 12,12 --> 8 + 10 + 12 + 12 = 42 第三行是 12 ,前一行 10,后两行 12 --> 10 + 12 + 12 = 34 第四行是 12 ,前一行 12,后两行没有 --> 12 + 12 = 24 --range SELECT *,sum(price) over(partition by type order by price range between 1 preceding and 2 following) as sum from commodity where type = '苹果'; 第一行 8 ,往前一个数值 8-1 = 7,往后两个数值 8+2 = 10 --> 7 <= price <= 10 --> 8 + 10 = 18 第二行 10 ,往前一个数值 10-1 = 9,往后两个数值 10+2 = 12 --> 9 <= price <= 12 --> 10 + 12 + 12 = 34 第三行 12 ,往前一个数值 12-1 = 11,往后两个数值 12+2 = 14 --> 11 <= price <= 14 --> 12 + 12 = 24 第四行 12 ,往前一个数值 12-1 = 11,往后两个数值 12+2 = 14 --> 11 <= price <= 14 --> 12 + 12 = 24 2.5 unbound 和 current row --在当前行往前1行,往后2行,一共4行范围内进行计算 rows between 1 preceding and 2 following --在当前行的数值往前1个数值,往后2个数值,进行计算,范围不一定,因为可能会出现重复值 range between 1 preceding and 2 following between … and … 后面的数字可以随着需求进行替换,当然也可以使用 unbound 和 current row ; 其中 unbounded 表示不做限制,current row 表示当前行 --按照分组内全部行求和,不做任何限制 rows between unbounded preceding and unbounded following --从分组内排序的起始行到当前行 rows between unbounded preceding and current row --按照分组内全部行求和,不做任何限制 range between unbounded preceding and unbounded following --从分组内排序的起始行的值到当前行的值 range between unbounded preceding and current row --rows between unbounded preceding and unbounded following SELECT *,sum(price) over(partition by type order by price rows between unbounded preceding and unbounded following) as sum from commodity where type = '苹果'; --rows between unbounded preceding and current row SELECT *,sum(price) over(partition by type order by price rows between unbounded preceding and current row) as sum from commodity where type = '苹果'; --range between unbounded preceding and unbounded following SELECT *,sum(price) over(partition by type order by price range between unbounded preceding and unbounded following) as sum from commodity where type = '苹果'; --range between unbounded preceding and current row SELECT *,sum(price) over(partition by type order by price range between unbounded preceding and current row) as sum from commodity where type = '苹果'; 2.6 first_value(), last_valus(), lag(), lead() first_value(字段) over(partition by … order by …) 求分组后的第一个值 last_value(字段) over(partition by … order by …) 求分组后的最后一个值 SELECT *,first_value(price) over(partition by type order by price) as mm from commodity; SELECT *,last_value(price) over(partition by type order by price) as mm from commodity; lag(expresstion,<offset>,<default>) over(partition by … order by …) 取出分组后前n行数据 lead(expresstion,<offset>,<default>) over(partition by … order by …) 取出分组后后n行数据 --取分组后的前两行数据/后两行数据, 默认值设置为 0 SELECT *,lag(price,2,0) over(partition by type order by price) as mm from commodity; SELECT *,lead(price,2,0) over(partition by type order by price) as mm from commodity; SELECT *,lag(price,1,0) over(partition by type order by price) as lag,lead(price,1,0) over(partition by type order by price) as lead from commodity; --第一个参数:要取的字段 --第二个参数:取排序后的第几条记录 --第三个参数:缺省值,如果后面的记录取不到值就默认取值第三个参数的值,注意参数的类型要与第一个参数所取字段的类型一致哦,话默认为空 注:具体的sql输出结果下文放置了建表语句,可以执行一下,自己体验体验!!! 2.7 preceding 和 following Hive函数, preceding:向前 following:向后,这两个窗口函数不仅可以实现滑窗求和(指定rows范围)或者指定范围内数据求和(指定range范围),也可以用来计算移动平均值: SELECT *,sum(price) over(partition by type order by price) as sum,avg(price) over(partition by type order by price) as avg,avg(price) over(partition by type order by price rows 2 preceding) as avg2 from commodity where type = '苹果'; 1 3.参考文献 SQL高级功能:窗口函数、存储过程及经典排名问题、topN问题等 分区函数Partition By的用法 SQL:聚合类窗口函数的preceding和following参数用法 4.建表语句 -- ---------------------------- -- Table structure for commodity -- ---------------------------- DROP TABLE IF EXISTS "public"."commodity"; CREATE TABLE "public"."commodity" ( "id" varchar(50) COLLATE "pg_catalog"."default" NOT NULL, "position" varchar(50) COLLATE "pg_catalog"."default", "type" varchar(50) COLLATE "pg_catalog"."default", "price" numeric(10,2) ) ; COMMENT ON COLUMN "public"."commodity"."id" IS '主键'; COMMENT ON COLUMN "public"."commodity"."position" IS '位置(商品放置的货架)'; COMMENT ON COLUMN "public"."commodity"."type" IS '类型'; COMMENT ON COLUMN "public"."commodity"."price" IS '价格'; -- ---------------------------- -- Records of commodity -- ---------------------------- INSERT INTO "public"."commodity" VALUES ('1', '1-001', '苹果', 8.00); INSERT INTO "public"."commodity" VALUES ('2', '2-002', '苹果', 10.00); INSERT INTO "public"."commodity" VALUES ('3', '3-003', '苹果', 12.00); INSERT INTO "public"."commodity" VALUES ('6', '1-001', '橘子', 5.00); INSERT INTO "public"."commodity" VALUES ('7', '1-001', '橙子', 6.00); INSERT INTO "public"."commodity" VALUES ('8', '3-003', '橙子', 8.00); INSERT INTO "public"."commodity" VALUES ('10', '2-002', '菠萝', 10.00); INSERT INTO "public"."commodity" VALUES ('9', '2-002', '香蕉', 5.00); INSERT INTO "public"."commodity" VALUES ('4', '1-001', '苹果', 12.00); INSERT INTO "public"."commodity" VALUES ('5', '1-001', '香蕉', 5.00); -- ---------------------------- -- Primary Key structure for table commodity -- ---------------------------- ALTER TABLE "public"."commodity" ADD CONSTRAINT "commodity_pkey" PRIMARY KEY ("id"); ———————————————— 原文链接:https://blog.csdn.net/weixin_44711823/article/details/135966741
-
group by 必须与聚合函数一起使用,最终使用的结果是将多行变成一行,默认取第一行,可能丢失数据partition by 也是分组,不过不会丢失数据,只是把数据做分组1. group by是分组函数,partition by是分析函数(然后像sum()等是聚合函数);2. 在执行顺序上,以下是常用sql关键字的优先级from > where > group by > having > order by而partition by应用在以上关键字之后,实际上就是在执行完select之后,在所得结果集之上进行partition。3.partition by相比较于group by,能够在保留全部数据的基础上,只对其中某些字段做分组排序(类似excel中的操作),而group by则只保留参与分组的字段和聚合函数的结果(类似excel中的pivot)。partition bygroup by4.如果在partition结果上聚合,千万注意聚合函数是逐条累计运行结果的!而在group by后的结果集上使用聚合函数,会作用在分组下的所有记录上。数据如下SQLselect a.cc,a.item,sum(a.num)from table_temp agroup by a.cc,a.itemResult111条记录经group by后为10条,其中cc='cn' and item='8.1.1'对应的两条记录的num汇总成值3.SQL2select a.cc,a.num, min(a.num) over (partition by a.cc order by a.num asc) as amountfrom table_temp agroup by a.cc,a.num;select a.cc,a.num, min(a.num) over (partition by a.cc order by a.num desc) as amountfrom table_temp agroup by a.cc,a.num;Result2两个sql的唯一区别在于a.num的排序上,但从结果红框中的数据对比可以看到amount值并不相同,且第二个结果集amount并不都是最小值1。在这里就是要注意将聚合函数用在partition后的结果集上时,聚合函数是逐条累积计算值的!其实partition by常同row_number() over一起使用,select a.*, row_number() over (partition by a.cc,a.item order by a.num desc) as seqfrom table_temp a两个sql的唯一区别在于a.num的排序上,但从结果红框中的数据对比可以看到amount值并不相同,且第二个结果集amount并不都是最小值1。在这里就是要注意将聚合函数用在partition后的结果集上时,聚合函数是逐条累积计算值的!SQL中只要用到聚合函数就一定要用到group by 吗?答:看情况1、当只做聚集函数查询时候,就不需要进行分组了。2、当聚集函数和非聚集函数出现在一起时,需要将非聚集函数进行group by举例来说:情况一:不需要使用Group by 进行分组,因为其中没有非聚合字段,所以不用Group by 也可以。SELECT SUM(bonus) FROM person情况二:SELECT SUM(bonus),gender FROM person GROUP BY gender由于gender是非聚合字段,Group by 后才可以正常执行。原文链接:https://blog.csdn.net/weixin_44547599/article/details/88764558
-
1.讲下对HashMap的认识 HashMap 存储的是键值对 key - value,key 具有唯一性,采用了链地址法来处理哈希冲突。当往 HashMap 中添加元素时,会计算 key 的 hash 值取余得出元素在数组中的的存放位置。 HashMap底层的数据结构在 JDK1.8 中有了较大的变化,1.8之前采用数组加链表的数据结构,1.8采用数组加链表加红黑树的数据结构。 HashMap 是线程不安全的,线程安全可以使用 HashTable 和 ConcurrentHashMap 。 在 1.8 版本的中 hash() 和 resize( ) 方法也有了很大的改变,提升了性能。 键和值都可存放null,键只能存放一个null,键为null时存放入table[0]。 2.HashMap的一些参数 //HashMap的默认初始长度16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //HashMap的最大长度2的30次幂 static final int MAXIMUM_CAPACITY = 1 << 30; //HashMap的默认加载因子0.75 static final float DEFAULT_LOAD_FACTOR = 0.75f; //HashMap链表升级成红黑树的临界值 static final int TREEIFY_THRESHOLD = 8; //HashMap红黑树退化成链表的临界值 static final int UNTREEIFY_THRESHOLD = 6; //HashMap链表升级成红黑树第二个条件:HashMap数组(桶)的长度大于等于64 static final int MIN_TREEIFY_CAPACITY = 64; //HashMap底层Node桶的数组 transient Node<K,V>[] table; //扩容阈值,当你的hashmap中的元素个数超过这个阈值,便会发生扩容 //threshold = capacity * loadFactor int threshold; 3.为什么HashMap的长度必须是2的n次幂? 在计算存入结点下标时,会利用 key 的 hsah 值进行取余操作,而计算机计算时,并没有取余等运算,会将取余转化为其他运算。 当n为2次幂时,会满足一个公式:(n - 1) & hash = hash % n,就可以用位运算代替取余运算,计算更加高效。 4.HashMap 为什么在获取 hash 值时要进行位运算 换种问法:能不能直接使用key的hashcode值计算下标存储? static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 如果使用直接使用hashCode对数组大小取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让 hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动。 (h >>> 16)是无符号右移16位的运算,右边补0,得到 hashCode 的高16位。 (h = key.hashCode()) ^ (h >>> 16) 把 hashCode 和它的高16位进行异或运算,可以使得到的 hash 值更加散列,尽可能减少哈希冲突,提升性能。 而这么来看 hashCode 被散列 (异或) 的是低16位,而 HashMap 数组长度一般不会超过2的16次幂,那么高16位在大多数情况是用不到的,所以只需要拿 key 的 HashCode 和它的低16位做异或即可利用高位的hash值,降低哈希碰撞概率也使数据分布更加均匀。 5.HashMap在JDK1.7和JDK1.8中有哪些不同? HashMap的底层实现 在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。 JDK1.8主要解决或优化了以下问题: resize 扩容和 计算hash 优化 引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。 6.HashMap的put方法的具体流程? 源码 HashMap是懒加载,只有在第一次put时才会创建数组。 总结 ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值 对,否则转向⑤; ⑤.遍历table[i],并记录遍历长度,如果遍历过程中发现key值相同的,则直接覆盖value,没有相同的key则在链表尾部插入结点,插入后判断该链表长度是否大等于8,大等于则考虑树化,如果数组的元素个数小于64,则只是将数组resize,大等于才树化该链表; ⑥.插入成功后,判断数组中的键值对数量size是否超过了阈值threshold,如果超过,进行扩容。 7.HashMap 的 get 方法的具体流程? public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //Node数组不为空,数组长度大于0,数组对应下标的Node不为空 if ((tab = table) != null && (n = tab.length) > 0 && //也是通过 hash & (length - 1) 来替代 hash % length 的 (first = tab[(n - 1) & hash]) != null) { //先和第一个结点比,hash值相等且key不为空,key的第一个结点的key的对象地址和值均相等 //则返回第一个结点 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //如果key和第一个结点不匹配,则看.next是否为空,不为null则继续,为空则返回null if ((e = first.next) != null) { //如果此时是红黑树的结构,则进行处理getTreeNode()方法搜索key if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); //是链表结构的话就一个一个遍历,直到找到key对应的结点, //或者e的下一个结点为null退出循环 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; } 总结 首先根据 hash 方法获取到 key 的 hash 值 然后通过 hash & (length - 1) 的方式获取到 key 所对应的Node数组下标 ( length对应数组长度 ) 首先判断此结点是否为空,是否就是要找的值,是则返回空,否则判断第二个结点是否为空,是则返回空,不是则判断此时数据结构是链表还是红黑树 链表结构进行顺序遍历查找操作,每次用 == 符号 和 equals( ) 方法来判断 key 是否相同,满足条件则直接返回该结点。链表遍历完都没有找到则返回空。 红黑树结构执行相应的 getTreeNode( ) 查找操作。 8.HashMap的扩容操作是怎么实现的? 不管是JDK1.7或者JDK1.8 当put方法执行的时候,如果table为空,则执行resize()方法扩容。默认长度为16。 JDK1.7扩容 条件:发生扩容的条件必须同时满足两点 当前存储的数量大于等于阈值 发生hash碰撞 因为上面这两个条件,所以存在下面这些情况 就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。 当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(这时元素个数小于阈值12,不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,所以不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。 特点:先扩容,再添加(扩容使用的头插法) 缺点:头插法会使链表发生反转,多线程环境下可能会死循环 扩容之后对table的调整: table容量变为2倍,所有的元素下标需要重新计算,newIndex = hash (扰动后) & (newLength - 1) JDK1.8扩容 条件: 当前存储的数量大于等于阈值 当某个链表长度>=8,但是数组存储的结点数size() < 64时 特点:先插后判断是否需要扩容(扩容时是尾插法) 缺点:多线程下,1.8会有数据覆盖 举例: 线程A:往index插,index此时为空,可以插入,但是此时线程A被挂起 线程B:此时,对index写入数据,A恢复后,就把B数据覆盖了 扩容之后对table的调整: table容量变为2倍,但是不需要像之前一样计算下标,只需要将hash值和旧数组长度相与即可确定位置。 如果 Node 桶的数据结构是链表会生成 low 和 high 两条链表,是红黑树则生成 low 和 high 两颗红黑树 依靠 (hash & oldCap) == 0 判断 Node 中的每个结点归属于 low 还是 high。 把 low 插入到 新数组中 当前数组下标的位置,把 high 链表插入到 新数组中 [当前数组下标 + 旧数组长度] 的位置 如果生成的 low,high 树中元素个数小于等于6退化成链表再插入到新数组的相应下标的位置 9.HashMap 在扩容时为什么通过位运算 (e.hash & oldCap) 得到下标? 从下图中我们可以看出,计算下标通过(n - 1) & hash,旧table的长度为16,hash值只与低四位有关,扩容后,table长度为32(两倍),此时只与低五位有关。 所以此时后几位的结果相同,前后两者之间的差别就差在了第五位上。 同时,扩容的时候会有 low 和 high 两条链表或红黑树来记录原来下标的数据和原来下标 + 旧table下标的数据。 如果第五位 b 是 0,那么只要看低四位 (也就是原来的下标);如果第五位是 1,只要把低四位的二进制数 + 1 0 0 0 0 ,就可以得到新数组下标。前面的部分刚好是原来的下标,后一部分就是旧table的长度 。那么我们就得出来了为什么把 low 插入扩容后 新数组[原来坐标] 的位置,把 high 插入扩容后 新数组[当前坐标 + 旧数组长度] 的位置。 那为什么根据 (e.hash & oldCap) == 0 来做判断条件呢?是因为旧数组的长度 length 的二进制数的第五位刚好是 1,hash & length 就可以计算 hash 值的第五位是 0 还是 1,就可以区别是在哪个位置上。 10.链表升级成红黑树的条件 链表长度大于8时才会考虑升级成红黑树,是有一个条件是 HashMap 的 Node 数组长度大于等于64(不满足则会进行一次扩容替代升级)。 11.红黑树退化成链表的条件 扩容 resize( ) 时,红黑树拆分成的 树的结点数小于等于临界值6个,则退化成链表。 删除元素 remove( ) 时,在 removeTreeNode( ) 方法会检查红黑树是否满足退化条件,与结点数无关。如果红黑树根 root 为空,或者 root 的左子树/右子树为空,root.left.left 根的左子树的左子树为空,都会发生红黑树退化成链表。 12.HashMap是怎么解决哈希冲突的? 使用链地址法(使用散列表)来链接拥有相同下标的数据; 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均; 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快; 13.HaspMap的初始化时数组长度和加载因子的约束范围 可以看到如果初始化数组长度 initialCapacity 小于 0 的话会跑出 IllegalArgumentException 的异常,initialCapacity 大于 MAXIMUM_CAPACITY 即 2 的 30 次幂的时候最大长度也只会固定在 MAXIMUM_CAPACITY ,在扩容的时候,如果数组的长度大等于MAXIMUM_CAPACITY,会将阈值设置为Integer.MAX_VALUE。 加载因子小于等于0时,或者加载因子是NaN时 (NaN 实际上就是 Not a Number的简称) 会抛出 IllegalArgumentException 的异常。 ———————————————— 原文链接:https://blog.csdn.net/qq_49217297/article/details/126304736
上滑加载中
推荐直播
-
华为云码道-AI时代应用开发利器2026/03/18 周三 19:00-20:00
童得力,华为云开发者生态运营总监/姚圣伟,华为云HCDE开发者专家
本次直播由华为专家带你实战应用开发,看华为云码道(CodeArts)代码智能体如何在AI时代让你的创意应用快速落地。更有华为云HCDE开发者专家带你用码道玩转JiuwenClaw,让小艺成为你的AI助理。
回顾中 -
Skill 构建 × 智能创作:基于华为云码道的 AI 内容生产提效方案2026/03/25 周三 19:00-20:00
余伟,华为云软件研发工程师/万邵业(万少),华为云HCDE开发者专家
本次直播带来两大实战:华为云码道 Skill-Creator 手把手搭建专属知识库 Skill;如何用码道提效 OpenClaw 小说文本,打造从大纲到成稿的 AI 原创小说全链路。技术干货 + OPC创作思路,一次讲透!
回顾中 -
码道新技能,AI 新生产力——从自动视频生成到开源项目解析2026/04/08 周三 19:00-21:00
童得力-华为云开发者生态运营总监/何文强-无人机企业AI提效负责人
本次华为云码道 Skill 实战活动,聚焦两大 AI 开发场景:通过实战教学,带你打造 AI 编程自动生成视频 Skill,并实现对 GitHub 热门开源项目的智能知识抽取,手把手掌握 Skill 开发全流程,用 AI 提升研发效率与内容生产力。
回顾中
热门标签